Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android面試知識(3)

Android面試知識(3)

編輯:關於Android編程

22、請解釋下在單線程模型中Message、Handler、Message Queue、Looper之間的關系。

Handler定義:

一個Handler允許你發送和處理Message和Runable對象,這些對象和一個線程的MessageQueue相關聯。每一個線程實例和一個單獨的線程以及該線程的MessageQueue相關聯。當你創建一個新的Handler時,它就和創建它的線程綁定在一起了。這裡,線程我們也可以理解為線程的MessageQueue。從這一點上來看,Handler把Message和Runable對象傳遞給MessageQueue,而且在這些對象離開MessageQueue時,Handler負責執行他們。

Handler 即為處理者,是Message的主要處理者,負責Message的發送,Message內容的執行處理

Message定義:

Message類就是定義了一個信息,這個信息中包含一個描述符和任意的數據對象,這個信息被用來傳遞給Handler.Message對象提供額外的兩個int域和一個Object域,這可以讓你在大多數情況下不用作分配的動作。
盡管Message的構造函數是public的,但是獲取Message實例的最好方法是調用Message.obtain(),或者Handler.obtainMessage()方法,這些方法會從回收對象池中獲取一個。

Message即為消息,可以理解為線程間交流的信息。處理數據後台線程需要更新UI,你可以發送內含一些數據的Message給UI線程。

Message Queue定義:

這是一個包含message列表的底層類。Looper負責分發這些message。Messages並不是直接加到一個MessageQueue中,而是通過Handler關聯到Looper。
你可以通過Looper.myQueue()從當前線程中獲取MessageQueue

Message Queue即為消息隊列,用來存放通過Handler發布的消息,按照先進先出原則執行。

每個Message Queue都會有一個對應的Handler。Handler會向Message Queue通過兩種方法發送消息:
sendMessage 或 post。這兩種消息都會插在Message Queue隊尾並按照先進先出原則執行。

但通過這兩種方法發送的消息執行的方式略有不同:
通過sendMessage發送的是一個Message對象,會被Handler的handleMessage()方法處理;
而通過post方法發送的是一個Runnable對象,則會自己執行。

Looper定義:

Looper類被用來執行一個線程中的message循環。默認情況,沒有一個消息循環關聯到線程。在線程中調用prepare()創建一個Looper,然後用loop()來處理messages,直到循環終止。

一個典型的帶有Looper的線程實現:

class LooperThread extends Thread {
      public Handler mHandler;
      public void run() {
          Looper.prepare();
          mHandler = newHandler() {
              publicvoidhandleMessage(Message msg) {
                  //process incomingmessages here
              }
          };
          Looper.loop();
      }
  }

Looper是每條線程裡Message Queue的管家。

Android沒有Global 的Message Queue,而Android 會自動替主線程(UI線程)建立Message Queue,但在子線程裡並沒有建立Message Queue。

所以調用Looper.getMainLooper()得到的主線程的Looper不為Null, 但調用Looper.myLooper()得到當前線程的Looper就有可能為Null。

23、AIDL的全稱是什麼?如何工作?能處理哪些類型的數據?

AIDL的英文全稱是Android Interface Define Language

只有當你允許來自不同的客戶端訪問你的服務並且需要處理多線程問題時你才必須使用AIDL。其他情況下你都可以選擇其他方法,如使用Messager,也能跨進程通訊(Messager是單線程處理)。

工作流程:
- 新建工程A,並定義IRemoteService.aidl文件

// IRemoteService.aidl
package com.example.android;
// Declare any non-default types here with import statements

/** Example service interface */
interface IRemoteService {
    /** Request the process ID of this service, to do evil things with it. */
    int getPid();

    /** Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}

build後會在相應目錄生成IRemoteService.java的文件,該類中含有一個名為IRemoteService.stub的內部類,該內部類中含有aidl文件接口的方法。

定義我們自己的Service類DDService.java,然後在AndroidManifest.xml中注冊,並添加“duanqing.test.aidl” 的ACTION。
package com.example.service;

import com.example.android.IRemoteService;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.Process;

public class DDService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
        System.out.println("DDService onCreate........" + "Thread: " + Thread.currentThread().getName());
    }
    @Override
    public IBinder onBind(Intent arg0) {
        System.out.println("DDService onBind");
        return mBinder;
    }

    private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
        public int getPid(){
            System.out.println("Thread: " + Thread.currentThread().getName());
            System.out.println("DDService getPid ");
            return Process.myPid();
        }
        public void basicTypes(int anInt, long aLong, boolean aBoolean,
            float aFloat, double aDouble, String aString) {
            System.out.println("Thread: " + Thread.currentThread().getName());
            System.out.println("basicTypes aDouble: " + aDouble +" anInt: " + anInt+" aBoolean " + aBoolean+" aString " + aString);
        }
    };
}

這樣服務端的開發就完成了。

實現客戶端測試代碼,我們另建一個工程B,同樣需要添加AIDL協議文件(將A工程的AIDL協議文件放置在B工程相應目錄路徑下或將生成的相關Java文件拷貝到B工程相應目錄路徑下)。
package com.example.aidlclient;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.view.View;

import com.example.android.IRemoteService;

public class MainActivity extends Activity {
    private IRemoteService remoteService;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    ServiceConnection conn = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            remoteService = IRemoteService.Stub.asInterface(service);
            try {
                int pid = remoteService.getPid();
                int currentPid = Process.myPid();
                System.out.println("currentPID: " + currentPid +"  remotePID: " + pid);
                remoteService.basicTypes(12, 1223, true, 12.2f, 12.3, "我們的愛,我明白");
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            System.out.println("bind success! " + remoteService.toString());
        }
    };

    /**
     * 監聽按鈕點擊
     * @param view
     */
    public void buttonClick(View view) {
        System.out.println("begin bindService");
        Intent intent = new Intent("duanqing.test.aidl");
        bindService(intent, conn, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);
    }
}
最後先運行A工程,再運行B工程,執行,看打印信息,可知道

當客戶端調用服務端getPid方法時,服務端是在Thread: Binder2中執行,當客戶端調用服務端basicType方法時,服務端是在Thread:Binder1中執行。

24、請解釋下Android程序運行時權限與文件系統權限的區別。

apk是運行在虛擬機上的,對應的是Android獨特的權限機制,,只有體現到文件系統上時才使用linux的權限設置。

linux文件系統上的權限:代表的是相應的用戶/用戶組及其他人對此文件的訪問權限,與此文件運行起來具有的權限完全不相關。

Android的權限規則:
1)Android中的apk必須簽名;
2)基於UserID的進程級別的安全機制:進程有獨立的地址空間,進程與進程間默認是不能互相訪問的,是一種很可靠的保護機制。 Android通過為每一個安裝在設備上的包(apk)分配唯一的linux userID來實現,名稱為”app_”加一個數字,比如app_43
不同的UserID,運行在不同的進程,所以apk之間默認便不能相互訪問。但是,在AndroidManifest.xml中利用sharedUserId屬性給不同的package分配相同的userID,並且使用同樣的簽名,通過這樣做,兩個package可以被當做同一個程序,
系統會分配給兩個程序相同的UserID。
3)默認apk生成的數據對外是不可見的。

25、系統上安裝了多種浏覽器,能否指定某浏覽器訪問指定頁面?請說明原由。

在Android程序中我們可以通過發送隱式Intent來啟動系統默認的浏覽器。如果手機本身安裝了多個浏覽器而又沒有設置默認浏覽器的話,系統將讓用戶選擇使用哪個浏覽器來打開鏈接。比如:

Intent intent = newIntent();
intent.setAction("android.intent.action.VIEW");
Uri content_url = Uri.parse("http://www.163.com");
intent.setData(content_url);
startActivity(intent);

要啟動指定的浏覽器,我們可以通過發送顯示Intent。比如啟動系統默認浏覽器:

Intent intent =newIntent();        
intent.setAction("android.intent.action.VIEW");    
Uri content_url =Uri.parse("http://www.163.com");   
intent.setData(content_url);           
intent.setClassName("com.android.browser","com.android.browser.BrowserActivity");   
//啟動uc浏覽器
//intent.setClassName("com.uc.browser", "com.uc.browser.ActivityUpdate“);
//啟動opera浏覽器
//intent.setClassName("com.opera.mini.android", "com.opera.mini.android.Browser")
//啟動qq浏覽器
//intent.setClassName("com.tencent.mtt", "com.tencent.mtt.MainActivity")
startActivity(intent);

26、你如何評價Android系統?優缺點。

優勢:
- 開放性
- 掙脫運營商的束縛
- 豐富的硬件選擇
- 不受任何限制的開發商
- 無縫結合的Google應用

不足:
- 安全和隱私
- 運營商仍然能夠影響到Android手機
- 同類機型用戶減少
- 過分依賴開發商缺少標准配置

27、什麼是ANR 如何避免它?

ANR即應用程序沒響應(Application Not Responding)
在Android裡,應用程序的響應性是由Activity Manager和Window Manager系統服務監視的。當出現以下情況時:
- 在5秒內沒有響應輸入的事件(例如,按鍵按下,屏幕觸摸);
- BroadcastReceiver在10秒內沒有執行完畢;

就可能引發ANR。

如何避免:將所有耗時操作,比如訪問網絡,Socket 通信,查詢大量 SQL 語句,復雜邏輯計算等都放在子線程中去,然後
通過 Handler.sendMessage()、runOnUITread()、AsyncTask 等方式更新 UI。無論如何都要確保用戶界面操作的流暢度。如果耗時操作需要讓用戶等待,那麼可以在界面上顯示進度條。

應用程序應該避免在BroadcastReceiver裡做耗時的操作或計算。但不再是在子線程裡做這些任務(因為BroadcastReceiver的生命周期短),若需要執行一個耗時的動作的話,應用程序應該啟動一個Service。

28、什麼情況會導致Force Close ?如何避免?能否捕獲導致其的異常?

拋出運行時異常時就會導致Force Close,比如空指針、數組越界、類型轉換異常等。

捕獲:可以通過logcat查看拋出異常的代碼出現的位置,然後到程序對應代碼中進行修改。

避免:編寫程序時,要思維缜密,在可能出現異常的地方都作相應的處理,增強程序的健壯性

也可以實現Thread.UncaughtExceptionHandler接口的uncaughtException方法來避免:

import java.lang.Thread.UncaughtExceptionHandler;
import android.app.Application;
public class MyApplication extends Application implements UncaughtExceptionHandler {
    @Override
    public void onCreate() {
    // TODO Auto-generated method stub
        super.onCreate();
    }

    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        thread.setDefaultUncaughtExceptionHandler( this);  
    }
}

想要哪個線程可以處理未捕獲異常,Thread.setDefaultUncaughtExceptionHandler( this)這句代碼都要在那個線程中執行一次。

29、Android本身的api並未聲明會拋出異常,則其在運行時有無可能拋出runtime異常,你遇到過嗎?諾有的話會導致什麼問題?如何解決?

有可能。比如NullPointerException。會導致程序無法正常運行出現force close。

解決:在程序中不確定的地方應該多進行if語句的判斷,適當加入try…catch語句進行異常的捕獲。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved