前言
写在前面:首先是不一次性放出来的原因:资料来之不易,希望大家好好珍惜,每天花一段时间细细的消化这些题目,其次希望大家在阅读题目的时候最好跟着书或者代码一起阅读、一起敲,做到熟稔于心,信手拈来,这样面试的时候才能展现你最自信的一面。
1、如何在Android应用程序中使用现有数据库?
答案:
注意: 在尝试此代码之前,请在以下代码中找到此行:
private static String DB_NAME ="YourDbName"; // Database name
DB_NAME
此处是你的数据库的名称。假设你在Assets
文件夹中有数据库的副本,因此,例如,如果你的数据库名称为ordersDB
,则DB_NAME
的值为orderDB
,
private static String DB_NAME ="ordersDB";
将数据库保留在资产文件夹中,然后执行以下操作:
DataHelper类:
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import android.content.Context;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class DataBaseHelper extends SQLiteOpenHelper {
private static String TAG = "DataBaseHelper"; // Tag just for the LogCat window
private static String DB_NAME ="YourDbName"; // Database name
private static int DB_VERSION = 1; // Database version
private final File DB_FILE;
private SQLiteDatabase mDataBase;
private final Context mContext;
public DataBaseHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
DB_FILE = context.getDatabasePath(DB_NAME);
this.mContext = context;
}
public void createDataBase() throws IOException {
// If the database does not exist, copy it from the assets.
boolean mDataBaseExist = checkDataBase();
if(!mDataBaseExist) {
this.getReadableDatabase();
this.close();
try {
// Copy the database from assests
copyDataBase();
Log.e(TAG, "createDatabase database created");
} catch (IOException mIOException) {
throw new Error("ErrorCopyingDataBase");
}
}
}
// Check that the database file exists in databases folder
private boolean checkDataBase() {
return DB_FILE.exists();
}
// Copy the database from assets
private void copyDataBase() throws IOException {
InputStream mInput = mContext.getAssets().open(DB_NAME);
OutputStream mOutput = new FileOutputStream(DB_FILE);
byte[] mBuffer = new byte[1024];
int mLength;
while ((mLength = mInput.read(mBuffer)) > 0) {
mOutput.write(mBuffer, 0, mLength);
}
mOutput.flush();
mOutput.close();
mInput.close();
}
// Open the database, so we can query it
public boolean openDataBase() throws SQLException {
// Log.v("DB_PATH", DB_FILE.getAbsolutePath());
mDataBase = SQLiteDatabase.openDatabase(DB_FILE, null, SQLiteDatabase.CREATE_IF_NECESSARY);
// mDataBase = SQLiteDatabase.openDatabase(DB_FILE, null, SQLiteDatabase.NO_LOCALIZED_COLLATORS);
return mDataBase != null;
}
@Override
public synchronized void close() {
if(mDataBase != null) {
mDataBase.close();
}
super.close();
}
}
编写一个DataAdapter类,例如:
import java.io.IOException;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
public class TestAdapter {
protected static final String TAG = "DataAdapter";
private final Context mContext;
private SQLiteDatabase mDb;
private DataBaseHelper mDbHelper;
public TestAdapter(Context context) {
this.mContext = context;
mDbHelper = new DataBaseHelper(mContext);
}
public TestAdapter createDatabase() throws SQLException {
try {
mDbHelper.createDataBase();
} catch (IOException mIOException) {
Log.e(TAG, mIOException.toString() + " UnableToCreateDatabase");
throw new Error("UnableToCreateDatabase");
}
return this;
}
public TestAdapter open() throws SQLException {
try {
mDbHelper.openDataBase();
mDbHelper.close();
mDb = mDbHelper.getReadableDatabase();
} catch (SQLException mSQLException) {
Log.e(TAG, "open >>"+ mSQLException.toString());
throw mSQLException;
}
return this;
}
public void close() {
mDbHelper.close();
}
public Cursor getTestData() {
try {
String sql ="SELECT * FROM myTable";
Cursor mCur = mDb.rawQuery(sql, null);
if (mCur != null) {
mCur.moveToNext();
}
return mCur;
} catch (SQLException mSQLException) {
Log.e(TAG, "getTestData >>"+ mSQLException.toString());
throw mSQLException;
}
}
}
现在你可以像这样使用它:
TestAdapter mDbHelper = new TestAdapter(urContext);
mDbHelper.createDatabase();
mDbHelper.open();
Cursor testdata = mDbHelper.getTestData();
mDbHelper.close();
对于Android 4.1(Jelly Bean),更改:
DB_PATH = "/data/data/" + context.getPackageName() + "/databases/";
至:
DB_PATH = context.getApplicationInfo().dataDir + "/databases/";
在DataHelper类中,此代码将在Jelly Bean 4.2多用户上运行。
编辑:代替使用硬编码路径,我们可以使用
DB_PATH = context.getDatabasePath(DB_NAME).getAbsolutePath();
这将为我们提供数据库文件的完整路径,并适用于所有Android版本
2、Android EditText最大长度
答案:
Android中EditText
的Limit text length
的可能重复
使用 android:maxLength="140"
那应该工作。😃
3、如何在Android中动态添加按钮?
答案:
Button myButton = new Button(this);
myButton.setText(“Push Me”);
LinearLayout ll = (LinearLayout)findViewById(R.id.buttonlayout);
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
ll.addView(myButton, lp);
4、下面代码输出的结果为:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Test{
public static void main(String[] args) {
List<Student> students = new ArrayList<Student>();
students.add(new Student(10));
students.add(new Student(18));
students.add(new Student(15));
Collections.sort(students);
System.out.print(students.get(1).age);
}
}
class Student implements Comparable<Student> {
Integer age;
public Student(Integer age) {
this.age = age;
}
public int compareTo(Student s) {
return s.age.compareTo(this.age);
}
}
答案:A
A. 15
B. 10
C. 18
D. 编译失败
别问我为什么没有5,因为5字不行。
6、JSON语法是否允许对象中有重复键?
答案:
从标准(第ii页):
可以预期,其他标准将严格遵循JSON文本格式来引用这一标准,同时对各种编码细节施加限制。此类标准可能需要特定的行为。JSON本身未指定任何行为。
在标准(第2页)中,进一步介绍了JSON对象的规范:
对象结构被表示为一对大括号标记,它们围绕零个或多个名称/值对。名称是一个字符串。每个名称后都有一个冒号,将名称与值分隔开。单个逗号标记将值与后面的名称分开。
它没有提及重复密钥无效或有效,因此根据规范,我可以放心地认为这意味着它们是允许的。
由于首引号,大多数JSON库实现不接受重复键并不与标准冲突。
这是与C ++标准库相关的两个示例。将某些JSON对象反序列化为时std::map,拒绝重复的键是有意义的。但是,当将某些JSON对象反序列化为a时std::multimap,可以像往常一样接受重复的键。
7、Android:通过BLE发送大于20个字节的数据
答案:
BLE最多允许你传输20个字节。
如果要发送20个以上的字节,则应定义数组byte[]
以包含所需的数据包数量。
如果你要发送少于160个字符(160个字节),以下示例可以很好地工作。
p / s:根据需要编辑以下内容。不要完全跟随我。
实际上,当我们使用BLE时,移动端和固件端需要设置密钥(例如0x03...
)来定义双方之间的连接门。
这个想法是:
- 当我们继续传输数据包时,不是最后一个。门是
byte[1] = 0x01
。 - 如果我们发送最后一个,则门为
byte[1] = 0x00
。
数据构造(20字节):
1– Byte 1
定义Gate ID
:例如。消息门ID byte[0] = 0x03
。
2- Byte 2
定义recognization
:是最后一个数据包0x00
还是继续发送数据包0x01。
3- Byte 3
(在消除Byte 1
&之后,应为18个字节Byte 2
)-在此处附加消息内容。
在阅读下面的代码之前,请先了解我的逻辑。
下面是发送带有许多数据包的消息的示例,每个数据包是一个大小为20字节的数组。
private void sendMessage(BluetoothGattCharacteristic characteristic, String CHARACTERS){
byte[] initial_packet = new byte[3];
/**
* Indicate byte
*/
initial_packet[0] = BLE.INITIAL_MESSAGE_PACKET;
if (Long.valueOf(
String.valueOf(CHARACTERS.length() + initial_packet.length))
> BLE.DEFAULT_BYTES_VIA_BLE) {
sendingContinuePacket(characteristic, initial_packet, CHARACTERS);
} else {
sendingLastPacket(characteristic, initial_packet, CHARACTERS);
}
}
private void sendingContinuePacket(BluetoothGattCharacteristic characteristic,
byte[] initial_packet, String CHARACTERS){
/**
* TODO If data length > Default data can sent via BLE : 20 bytes
*/
// Check the data length is large how many times with Default Data (BLE)
int times = Byte.valueOf(String.valueOf(
CHARACTERS.length() / BLE.DEFAULT_BYTES_IN_CONTINUE_PACKET));
Log.i(TAG, "CHARACTERS.length() " + CHARACTERS.length());
Log.i(TAG, "times " + times);
// TODO
// 100 : Success
// 101 : Error
byte[] sending_continue_hex = new byte[BLE.DEFAULT_BYTES_IN_CONTINUE_PACKET];
for (int time = 0; time <= times; time++) {
/**
* Wait second before sending continue packet
*/
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (time == times) {
Log.i(TAG, "LAST PACKET ");
/**
* If you do not have enough characters to send continue packet,
* This is the last packet that will be sent to the band
*/
/**
* Packet length byte :
*/
/**
* Length of last packet
*/
int character_length = CHARACTERS.length()
- BLE.DEFAULT_BYTES_IN_CONTINUE_PACKET*times;
initial_packet[1] = Byte.valueOf(String.valueOf(character_length
+ BLE.INITIAL_MESSAGE_PACKET_LENGTH));
initial_packet[2] = BLE.SENDING_LAST_PACKET;
Log.i(TAG, "character_length " + character_length);
/**
* Message
*/
// Hex file
byte[] sending_last_hex = new byte[character_length];
// Hex file : Get next bytes
for (int i = 0; i < sending_last_hex.length; i++) {
sending_last_hex[i] =
CHARACTERS.getBytes()[sending_continue_hex.length*time + i];
}
// Merge byte[]
byte[] last_packet =
new byte[character_length + BLE.INITIAL_MESSAGE_PACKET_LENGTH];
System.arraycopy(initial_packet, 0, last_packet,
0, initial_packet.length);
System.arraycopy(sending_last_hex, 0, last_packet,
initial_packet.length, sending_last_hex.length);
// Set value for characteristic
characteristic.setValue(last_packet);
} else {
Log.i(TAG, "CONTINUE PACKET ");
/**
* If you have enough characters to send continue packet,
* This is the continue packet that will be sent to the band
*/
/**
* Packet length byte
*/
int character_length = sending_continue_hex.length;
/**
* TODO Default Length : 20 Bytes
*/
initial_packet[1] = Byte.valueOf(String.valueOf(
character_length + BLE.INITIAL_MESSAGE_PACKET_LENGTH));
/**
* If sent data length > 20 bytes (Default : BLE allow send 20 bytes one time)
* -> set 01 : continue sending next packet
* else or if after sent until data length < 20 bytes
* -> set 00 : last packet
*/
initial_packet[2] = BLE.SENDING_CONTINUE_PACKET;
/**
* Message
*/
// Hex file : Get first 17 bytes
for (int i = 0; i < sending_continue_hex.length; i++) {
Log.i(TAG, "Send stt : "
+ (sending_continue_hex.length*time + i));
// Get next bytes
sending_continue_hex[i] =
CHARACTERS.getBytes()[sending_continue_hex.length*time + i];
}
// Merge byte[]
byte[] sending_continue_packet =
new byte[character_length + BLE.INITIAL_MESSAGE_PACKET_LENGTH];
System.arraycopy(initial_packet, 0, sending_continue_packet,
0, initial_packet.length);
System.arraycopy(sending_continue_hex, 0, sending_continue_packet,
initial_packet.length, sending_continue_hex.length);
// Set value for characteristic
characteristic.setValue(sending_continue_packet);
}
// Write characteristic via BLE
mBluetoothGatt.writeCharacteristic(characteristic);
}
}
public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic,
String data) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return false;
}
if (ActivityBLEController.IS_FIRST_TIME) {
/**
* In the first time,
* should send the Title
*/
byte[] merge_title = sendTitle(data);
// Set value for characteristic
characteristic.setValue(merge_title);
// Write characteristic via BLE
mBluetoothGatt.writeCharacteristic(characteristic);
// Reset
ActivityBLEController.IS_FIRST_TIME = false;
return true;
} else {
/**
* In the second time,
* should send the Message
*/
if (data.length() <= BLE.LIMIT_CHARACTERS) {
sendMessage(characteristic, data);
// Reset
ActivityBLEController.IS_FIRST_TIME = true;
return true;
} else {
// Typed character
typed_character = data.length();
return false;
}
}
}
8、下列关于Android目录结构说明错误的是
答案:D
A. assest下主要存放资源文件,如字体库,db库等
B. anim下主要存放一些以XML方式存在的动画源文件
C. values下可以放诸如string,attrs,style等各种属性,样式文件
D. drawable有很多个文件夹,根据手机屏幕密度依次增高,对应的图片文件夹分别是drawable-mdpi, drawable-ldpi, drawable-hdpi, drawable-xhdpi, drawable-xxhdpi
9、将FFmpeg与Android-NDK结合使用
答案:
如果您需要进一步的帮助,请确切说明您要实现的目标以及不起作用的目标…
更新-根据评论:
要在Android上通过命令行使用ffmpeg,您需要将ffmpeg
复制到应用程序的files目录中,chmod 755然后使用getRuntime.exec()ffmpeg
通过以下行运行:
Process p = Runtime.getRuntime().exec("/data/data/yourpackagename/files/ffmpeg -i infile.mp4 outfile.mp4")
10、如何在Android中以编程方式拒绝通话
答案:
首先创建此接口:
public interface ITelephony {
boolean endCall();
void answerRingingCall();
void silenceRinger();
}
然后创建扩展BroadcastReceiver的此类
public class IncomingCallReceiver extends BroadcastReceiver {
private ITelephony telephonyService;
private String blacklistednumber = "+458664455";
@Override
public void onReceive(Context context, Intent intent) {
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
try {
Class c = Class.forName(tm.getClass().getName());
Method m = c.getDeclaredMethod("getITelephony");
m.setAccessible(true);
ITelephony telephonyService = (ITelephony) m.invoke(tm);
Bundle bundle = intent.getExtras();
String phoneNumber = bundle.getString("incoming_number");
Log.e("INCOMING", phoneNumber);
if ((phoneNumber != null) && phoneNumber.equals(blacklistednumber)) {
telephonyService.silenceRinger();
telephonyService.endCall();
Log.e("HANG UP", phoneNumber);
}
} catch (Exception e) {
e.printStackTrace();
}
}
这只会阻止该单个电话号码,但是您明白了。
在清单中添加以下内容:
<receiver android:name=".IncomingCallReceiver">
<intent-filter android:priority="999">
<action android:name="android.intent.action.PHONE_STATE" />
</intent-filter>
</receiver>
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.PROCESS_INCOMING_CALLS" />
11、Android中“ @ id /”和“ @ + id /”之间的区别
答案:
您指的是Android resources
,它们已经在Android
系统中定义,并且@android:id/..
在访问您在项目中定义/创建的资源时,您可以使用@id/..
更多信息
根据您在聊天室中所做的澄清,您说自己有这样的问题:
如果我们使用android:id=”@id/layout_item_id”它不起作用。而是@+id/可以工作,所以这里有什么区别?那是我最初的问题。
好吧,这取决于上下文,当您使用的XML属性时android:id,您要指定一个新的ID,并指示解析器(或称其为builder)在中创建新条目R.java,因此您必须包括一个+标志。
在另一种情况下,例如android:layout_below="@id/myTextView"
,您指的是已经创建的ID,因此解析器将此链接到中已创建的ID R.java。
再次获得更多信息
正如您在聊天中所说的那样,请注意,如果android:layout_below="@id/myTextView"ID myTextView
是在使用元素的元素之后写入的,则该元素将无法识别。
12、Android使用AES进行加密/解密
private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(clear);
return encrypted;
}
private static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(encrypted);
return decrypted;
}
并像这样调用它们:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.PNG, 100, baos); // bm is the bitmap object
byte[] b = baos.toByteArray();
byte[] keyStart = "this is a key".getBytes();
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(keyStart);
kgen.init(128, sr); // 192 and 256 bits may not be available
SecretKey skey = kgen.generateKey();
byte[] key = skey.getEncoded();
// encrypt
byte[] encryptedData = encrypt(key,b);
// decrypt
byte[] decryptedData = decrypt(key,encryptedData);
这应该可行,我现在在项目中使用类似的代码。
13、Java 在Android中发送POST数据
答案:
public class CallAPI extends AsyncTask<String, String, String> {
public CallAPI(){
//set context variables if required
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected String doInBackground(String... params) {
String urlString = params[0]; // URL to call
String data = params[1]; //data to post
OutputStream out = null;
try {
URL url = new URL(urlString);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
out = new BufferedOutputStream(urlConnection.getOutputStream());
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"));
writer.write(data);
writer.flush();
writer.close();
out.close();
urlConnection.connect();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
14、如何发现Android中应用程序的内存使用情况?
答案:
请注意,现代操作系统(如Linux)上的内存使用是一个极其复杂且难以理解的领域。实际上,你正确解释任何数字的机会非常低。(几乎每当我与其他工程师一起查看内存使用量数字时,对于它们的实际含义总会进行长时间的讨论,这只会导致模糊的结论。)
注意:我们现在有更多有关管理应用程序内存的文档,其中涵盖了许多内容,并且是有关Android状态的最新信息。
第一件事可能是阅读本文的最后部分,该部分讨论了如何在Android上管理内存:
从Android 2.0开始更改服务API
现在ActivityManager.getMemoryInfo()
是我们用于查看整体内存使用情况的最高级别的API。这主要是为了帮助应用程序评估系统将如何接近没有更多内存供后台进程使用,因此需要开始杀死所需的进程(如服务)。对于纯Java应用程序,这应该没有多大用处,因为Java堆限制在某种程度上避免了一个应用程序能够使系统承受这一压力。
进入较低级别,你可以使用Debug API获得有关内存使用情况的原始内核级别信息:android.os.Debug.MemoryInfo
请注意,从2.0开始,还有一个API ActivityManager.getProcessMemoryInfo
来获取有关另一个进程的此信息:ActivityManager.getProcessMemoryInfo(int [])
这将返回具有所有以下数据的低级MemoryInfo结构:
/** The proportional set size for dalvik. */
public int dalvikPss;
/** The private dirty pages used by dalvik. */
public int dalvikPrivateDirty;
/** The shared dirty pages used by dalvik. */
public int dalvikSharedDirty;
/** The proportional set size for the native heap. */
public int nativePss;
/** The private dirty pages used by the native heap. */
public int nativePrivateDirty;
/** The shared dirty pages used by the native heap. */
public int nativeSharedDirty;
/** The proportional set size for everything else. */
public int otherPss;
/** The private dirty pages used by everything else. */
public int otherPrivateDirty;
/** The shared dirty pages used by everything else. */
public int otherSharedDirty;
但是关于,和之间的区别是什么Pss
,现在好了。PrivateDirtySharedDirty
实际上,Android
(通常是Linux
系统)中的许多内存在多个进程之间共享。因此,一个进程使用多少内存确实不清楚。在将页面调度到磁盘的基础上再加上磁盘(更不用说我们在Android上不使用的交换)了,这甚至还不清楚。
因此,如果你要获取实际映射到每个进程中的所有物理RAM
,并将所有进程相加,那么最终结果可能会比实际总RAM大得多。
该Pss
数字是内核计算的考虑内存共享的度量标准-基本上,一个进程中RAM的每个页面都是按也使用该页面的其他进程数的比例来缩放的。这样,你可以(理论上)将所有进程的pss
相加,以查看它们正在使用的总RAM
,并比较各个进程之间的pss
,以大致了解它们的相对权重。
另一个有趣的指标是PrivateDirty
,它基本上是进程内无法分页到磁盘(不由磁盘上的相同数据支持)并且不与任何其他进程共享的RAM量。另一种看待这种情况的方式是,当该进程消失时,RAM将可供系统使用(并且可能很快被包含在缓存和其他用途中)。
这差不多就是SDK API。但是,作为设备开发人员,你可以做更多的事情。
使用adb
,你可以获得许多有关正在运行的系统的内存使用情况的信息。常见的命令adb shell dumpsys meminfo
是吐出有关每个Java进程的内存使用信息的信息,其中包含上述信息以及其他各种信息。你还可以添加单个进程的名称或pid来查看,例如,adb shell dumpsys meminfo system
给我系统进程:
** MEMINFO in pid 890 [system] **
native dalvik other total
size: 10940 7047 N/A 17987
allocated: 8943 5516 N/A 14459
free: 336 1531 N/A 1867
(Pss): 4585 9282 11916 25783
(shared dirty): 2184 3596 916 6696
(priv dirty): 4504 5956 7456 17916
Objects
Views: 149 ViewRoots: 4
AppContexts: 13 Activities: 0
Assets: 4 AssetManagers: 4
Local Binders: 141 Proxy Binders: 158
Death Recipients: 49
OpenSSL Sockets: 0
SQL
heap: 205 dbFiles: 0
numPagers: 0 inactivePageKB: 0
activePageKB: 0
顶部是主要部分,其中size是特定堆的地址空间中的总大小,allocated是堆认为具有的实际分配的kb,free是堆可用于其他分配的剩余kb,并且pss与priv dirty相同如前所述,专门针对与每个堆相关的页面。
如果只想查看所有进程的内存使用情况,则可以使用命令adb shell procrank。在同一系统上的输出如下所示:
PID Vss Rss Pss Uss cmdline
890 84456K 48668K 25850K 21284K system_server
1231 50748K 39088K 17587K 13792K com.android.launcher2
947 34488K 28528K 10834K 9308K com.android.wallpaper
987 26964K 26956K 8751K
7308K 24308s 24com google.google.com .android.phone
948 23020K 23016K 5864K 4748K com.android.inputmethod.latin
888 25728K 25724K 5774K 3668K zygote
977 24100K 24096K 5667K 4340K android.process.acore
...
59 336K 332K 99K 92K / system / bin /
installed 60 396K 392K 93K 84K / system / bin / keystore
51 280K 276K 74K 68K / system / bin / servicemanager
54 256K 252K 69K 64K / system / bin / debuggerd
这里的Vss
和Rss
列基本上是噪音(这些是进程的直接地址空间和RAM使用情况,如果你将各个进程之间的RAM使用情况相加,则会得到非常高的数字)。
Pss
正如我们之前所见,并且Uss
是Priv Dirty
。
在此需要注意的有趣事情:Pss
与Uss
我们在中看到的内容略有不同(或略有不同)meminfo。这是为什么?好procrank
使用的内核机制与收集数据的机制不同meminfo,它们给出的结果略有不同。这是为什么?老实说我没头绪。我相信procrank
可能是更准确的……但是,实际上,这就是要说的:“获取一粒盐得到的任何存储信息;通常是非常大的一粒。”
最后,该命令adb shell cat /proc/meminfo提供了系统整体内存使用情况的摘要。这里有很多数据,只有前几个值得讨论(剩下的几个人很少理解,而我对这几个人的问题经常会引起相互矛盾的解释):
MemTotal: 395144 kB
MemFree: 184936 kB
Buffers: 880 kB
Cached: 84104 kB
SwapCached: 0 kB
MemTotal 是内核和用户空间可用的内存总量(通常少于设备的实际物理RAM,因为无线电,DMA缓冲区等需要一些RAM)。
MemFree 是根本不使用的RAM数量。你在这里看到的数字很高;通常在Android系统上只有几MB,因为我们尝试使用可用内存来保持进程运行
Cached 是用于文件系统缓存和其他此类事物的RAM。典型的系统将需要大约20MB的内存,以避免陷入不良的分页状态。针对特定系统对Android内存不足杀手进行了调整,以确保在后台进程被缓存RAM占用过多而导致这种分页之前,后台进程被杀死。
15、Android自定义事件监听器
答案:
public class CustomView extends View(){
OnCustomEventListener mListener;
:
://some code
:
:
创建一个将由您的活动实现的接口:
public interface OnCustomEventListener {
void onEvent();
}
public void setCustomEventListener(OnCustomEventListener eventListener) {
mListener = eventListener;
}
现在,您需要知道事件实际发生的时间。例如,当用户触摸屏幕上的一个点时,重写onTouchEvent方法:
onTouchEvent(MotionEvent ev) {
if (ev.getAction==MotionEvent.ACTION_DOWN) {
if(mListener!=null)
mListener.onEvent();
}
}
同样,您可以创建所需的特定事件。(示例可能是按下,等待2秒钟然后松开-您需要在touch事件中执行一些逻辑操作)。
在您的活动中,可以使用customView对象将eventListener设置为:
customView.setCustomEventListener(new OnCustomEventListener() {
public void onEvent() {
//do whatever you want to do when the event is performed.
}
});
16、LinearLayout,RelativeLayout和AbsoluteLayout有什么区别?
答案:
LinearLayout
表示您可以一一对齐视图(垂直/水平)。
RelativeLayout
指基于其父母的观点与其他观点之间的关系。
ConstraintLayout
与RelativeLayout
的相似之处在于它使用关系来定位和调整尺寸小部件,但具有更大的灵活性,并且更易于在布局编辑器中使用。
WebView
加载html,静态或动态页面。
FrameLayout 要将孩子放在另一个上面,就像卡片在框架内一样,我们可以将一个孩子放在另一个之上或框架内的任何位置。
不推荐使用- AbsoluteLayout
意味着您必须给出视图应位于的确切位置。
17、使用RxJava处理分页
答案:
您可以递归建模:
Observable<ApiResponse> getPageAndNext(int page) {
return getResults(page)
.concatMap(new Func1<ApiResponse, Observable<ApiResponse>>() {
@Override
public Observable<ApiResponse> call(ApiResponse response) {
// Terminal case.
if (response.next == null) {
return Observable.just(response);
}
return Observable.just(response)
.concatWith(getPageAndNext(response.next));
}
});
}
然后,要消耗它,
getPageAndNext(0)
.concatMap(new Func1<ApiResponse, Observable<ResponseObject>>() {
@Override
public Observable<ResponseObject> call(ApiResponse response) {
return Observable.from(response.results);
}
})
.subscribe(new Action1<ResponseObject>() { /** Do something with it */ });
那应该使您有一个流ResponseObject将按顺序到达,并且很可能以页面大小的块到达。
18、计算机和手机之间的蓝牙客户端/服务器通信
答案:
我只需要一个使手机和计算机通过蓝牙进行通信的基本示例或教程即可。
一个良好的链接/网站开始也受到赞赏。
服务器部分(计算机部分)可以用多种编程语言(Java,C ++,Python甚至其他语言)制作。
客户端部件(手机)必须是Java Micro Edition MIDlet。
经过数天的研究,我设法制作了一个应用程序,可以将计算机上鼠标光标周围区域的屏幕截图实时发送到手机客户端。
客户端和服务器都是用Java编写的。我使用BlueCove制作了桌面部件。它是JSR-82的实现(与MIDP中的协议相同),因此台式机和移动部件共享大量代码。
万一有人需要,我会将应用程序发布为公共领域,不承担任何责任。
我并不为此感到骄傲。毕竟,我写这本书的时候才15岁。在某些地方,代码太可怕了,但是我很喜欢为代码重用做出抽象。
19、如何在Android中声明全局变量?
我在09年Android相对较新的时候就写下了这个答案,并且在Android开发中有很多尚未完善的领域。我在本文的底部添加了很长的附录,以解决一些批评,并详细说明了我对使用Singleton而不是对Application进行子类化时的哲学分歧。阅读它需要您自担风险。
原始答案:
您遇到的更普遍的问题是如何在多个活动和应用程序的所有部分之间保存状态。静态变量(例如,单例)是实现此目的的常见Java方法。但是,我发现,Android中一种更优雅的方法是将您的状态与Application上下文相关联。
如您所知,每个Activity也是一个Context,它是广义上有关其执行环境的信息。您的应用程序还具有上下文,Android保证它会在您的应用程序中作为单个实例存在。
这样做的方法是创建自己的android.app.Application子类,然后在清单的application标记中指定该类。现在,Android将自动创建该类的实例,并将其用于整个应用程序。可以从任何访问它context使用Context.getApplicationContext()
方法(Activity
还提供了一种方法getApplication()
,其具有完全相同的效果)。以下是一个极其简化的示例,但要注意以下几点:
class MyApp extends Application {
private String myState;
public String getState(){
return myState;
}
public void setState(String s){
myState = s;
}
}
class Blah extends Activity {
@Override
public void onCreate(Bundle b){
...
MyApp appState = ((MyApp)getApplicationContext());
String state = appState.getState();
...
}
}
这与使用静态变量或单例具有基本相同的效果,但可以很好地集成到现有的Android框架中。请注意,这将无法跨进程使用(如果您的应用程序是具有多个进程的稀有应用程序之一)。
上面的示例中有一些注意事项;假设我们做了类似的事情:
class MyApp extends Application {
private String myState = /* complicated and slow initialization */;
public String getState(){
return myState;
}
}
现在,每次实例化应用程序时,都会执行这种缓慢的初始化(例如,击中磁盘,击中网络,任何阻塞等)!您可能会认为,这只是该过程的一次,无论如何我都必须支付费用,对吗?例如,正如黛安·哈克伯恩(Dianne Hackborn)在下面提到的那样,完全有可能实例化您的流程,以处理后台广播事件。如果您的广播处理不需要这种状态,则可能只是无所事事地完成了一系列复杂而缓慢的操作。延迟实例化是此处的游戏名称。以下是使用应用程序的稍微复杂的方法,除了最简单的使用方法之外,其他方法都更有意义:
class MyApp extends Application {
private MyStateManager myStateManager = new MyStateManager();
public MyStateManager getStateManager(){
return myStateManager ;
}
}
class MyStateManager {
MyStateManager() {
/* this should be fast */
}
String getState() {
/* if necessary, perform blocking calls here */
/* make sure to deal with any multithreading/synchronicity issues */
...
return state;
}
}
class Blah extends Activity {
@Override
public void onCreate(Bundle b){
...
MyStateManager stateManager = ((MyApp)getApplicationContext()).getStateManager();
String state = stateManager.getState();
...
}
}
尽管我更喜欢Application子类,而不是在这里使用单例作为更优雅的解决方案,但我宁愿开发人员在确实需要时使用Singleton,而不是完全不考虑将状态与Application子类相关联的性能和多线程含义。
注意1:同样,如反咖啡厅所评论,为了正确地将应用程序替代项与应用程序相关联,清单文件中必须有一个标记。同样,请参阅Android文档以获取更多信息。一个例子:
<application
android:name="my.application.MyApp"
android:icon="..."
android:label="...">
</application>
注意2: user608578在下面询问如何与管理本机对象生命周期一起工作。我丝毫没有加快在Android上使用本机代码的速度,也没有资格回答如何与我的解决方案进行交互。如果有人对此有答案,我愿意将其归功于此,并将信息放在此文章中,以实现最大的可视性。
附录:
正如某些人指出的那样,这不是持久状态的解决方案,我也许应该在原始答案中更加强调这一点。即,这并不意味着它是一种用于保存用户或其他信息的解决方案,这些信息或信息将在应用程序的整个生命周期中保持不变。因此,我认为下面的大多数批评都与应用程序在任何时候被杀死等无关,因为任何需要保留到磁盘的内容都不应通过Application子类存储。它旨在成为一种解决方案,用于存储临时的,易于重新创建的应用程序状态(例如,是否已登录用户)和本质上是单个实例的组件(例如,应用程序网络管理器)(不是单例!)。
Dayerman一直很友善地指出与Reto Meier和Dianne Hackborn的有趣对话,在该对话中,不鼓励使用Application子类来支持Singleton模式。Somatik还早些时候指出了这种性质的东西,尽管当时我没有看到。由于Reto和Dianne在维护Android平台方面所扮演的角色,因此我不能真诚地建议您忽略他们的建议。他们说的去。我确实不同意关于优先选择Singleton而不是Application子类的观点。在我的不同意中,我将利用在StackExchange对Singleton设计模式的解释中最好地解释的概念,这样我就不必在此答案中定义术语。我强烈建议先跳过链接,然后再继续。逐点:
Dianne指出:“没有理由从Application进行子类化。这与创建单例没有什么不同……”第一个主张是不正确的。这有两个主要原因。1)Application类为应用程序开发人员提供了更好的生命周期保证;它保证了应用程序的生命周期。单例并没有明确地依赖于应用程序的生命周期(尽管有效)。对于您的普通应用程序开发人员来说,这可能不是问题,但是我认为这正是Android API应该提供的合同类型,并且通过最大程度地减少关联的生命周期,它还为Android系统提供了更大的灵活性。数据。2)Application类为应用程序开发人员提供了状态的单个实例持有者,这与单身汉的州持有人有很大的不同。有关差异的列表,请参见上面的Singleton说明链接。
Dianne继续说道:“ …将来很可能会后悔,因为您发现您的Application对象变成了本应独立的应用程序逻辑的一团糟。” 这当然不是不正确的,但这不是选择Singleton而不是Application子类的原因。Diane的论点都没有提供使用Singleton比Application子类更好的理由,她试图建立的唯一理由是使用Singleton并不比Application子类差,我认为这是错误的。
她继续说:“这更自然地导致您应该如何管理这些东西-根据需要初始化它们。” 这忽略了以下事实:您也没有理由也无法使用Application子类按需初始化。再次没有区别。
Dianne最后说:“框架本身为应用程序维护的所有少量共享数据都有成千上万的单例,例如已加载资源的缓存,对象池等。它运作良好。” 我不是在争论使用Singletons不能正常工作或不是合法的选择。我在争辩说,Singletons与Android系统之间的合同不如使用Application子类强大,而且使用Singletons通常指向不灵活的设计,这种设计不容易修改,并且会导致很多问题。恕我直言,Android API为开发人员应用程序提供的强大合同是使用Android进行编程的最吸引人和令人愉悦的方面之一,它有助于导致早期开发人员的采用,从而推动了Android平台取得今天的成功。
Dianne在下面也进行了评论,并提到了使用Application子类的其他缺点,它们可能会鼓励或简化编写性能较低的代码的过程。这是真的,我已经编辑了此答案,以强调此处考虑性能的重要性,如果您正在使用应用程序子类化,则应采用正确的方法。正如Dianne所说,重要的是要记住,即使只为后台广播而加载该进程,您的Application类也会在每次加载该进程时实例化(如果您的应用程序在多个进程中运行,则可能一次被多次!)。事件。因此,重要的是将Application类更多地用作指向应用程序共享组件的指针的存储库,而不是用作进行任何处理的地方!
我留下了以下从SingleStack链接中窃取的Singletons的缺点:
- 无法使用抽象或接口类;
- 无法分类;
- 整个应用程序之间的高度耦合(难以修改);
- 难以测试(在单元测试中不能伪造/模拟);
- 在可变状态下很难并行化(需要广泛的锁定);
并添加我自己的:
- 不清楚且难以管理的终身合同,不适合Android(或大多数其他)开发;
20、何时调用活动上下文或应用程序上下文?
答案:
getApplicationContext()
几乎总是错的。Hackborn
女士(等等)已经非常明确,你只用getApplicationContext()
当你知道为什么你使用getApplicationContext()
,只有当你需要使用getApplicationContext()
。
直言不讳,“某些程序员”之所以使用getApplicationContext()(或getBaseContext()程度较小)是因为他们的Java经验有限。他们实现了一个内部类(例如,in中的OnClickListenerfor ),并且需要a 。他们使用或获取对象,而不是使用“获取外部类” 。ButtonActivityContextMyActivity.thisthisgetApplicationContext()getBaseContext()Context
你只用getApplicationContext()
当你知道你需要Context的东西,可以活得比其他任何可能Context您在您的处置。场景包括:
使用getApplicationContext()
,如果你需要的东西绑在Context
本身将具有全局作用域。我在中使用getApplicationContext()
,例如,WakefulIntentService
将静态WakeLock用于服务。既然这WakeLock
是静态的,并且我需要Context
先PowerManager
进行创建,那么使用它是最安全的getApplicationContext()
。
如果要通过绑定实例之间的(即绑定的句柄),则从getApplicationContext()绑定到时使用。Android会通过这些内部跟踪绑定,并保留对创建绑定的的引用。如果从绑定,则新实例将具有对的引用,而对则具有对旧的隐式引用,并且不能对旧实例进行垃圾回收。ServiceActivityServiceConnectionActivityonRetainNonConfigurationInstance()ServiceConnectionsContextsActivityActivityServiceConnectionActivityActivity
一些开发人员将自定义子类的Application用作自己的全局数据,然后通过进行检索getApplicationContext()。当然有可能。我更喜欢静态数据成员,如果出于其他原因,您只能拥有一个自定义Application对象。我使用一个自定义Application对象构建了一个应用程序,发现它很痛苦。哈克伯恩女士也同意这一立场。
以下是无论您在何处都不使用的原因getApplicationContext()
:
它不是一个完整的功能Context
,支持所有Activity功能。您将尝试执行的各种操作Context
将失败,主要与GUI有关。
如果ContextfromgetApplicationContext()
保留了您不清除的调用所创建的某些内容,则可能导致内存泄漏。使用Activity,如果它保留了某物,则一旦Activity收集到垃圾,其他所有内容也会被清除。该Application
对象将在过程的整个生命周期中保留。