Context實際上用來管理訪問全局資源
getContext().getResources().getXXX
這行代碼就可以訪問到全局的字符串、圖片等資源
例如給一個TextView設置內容:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView = new TextView(this);
//訪問到全局資源:字符串hello_world
textView.setText(R.string.hello_world);
setContentView(textView);
}
上面這個TextView**引用**了this,也就是MainActivity這個context,所以能訪問到全局資源,比如R.string.hello_world。
看一下源碼
@android.view.RemotableViewMethod
public final void setText(@StringRes int resid) {
setText(getContext().getResources().getText(resid));
}
可以看到這句代碼可以訪問資源,而由於TextView在構造的時候傳了this進去,getContext()
返回的就是當前的MainActivity。
又例如ImageView也可以這麼做
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView imageView = new ImageView(this);
//訪問ic_launcher這個圖片資源
imageView.setImageResource(R.mipmap.ic_launcher);
setContentView(imageView);
}
從這裏開始就是收費課程了,30軟一個月還蠻便宜的其實
Application纔是真正的全局上下文對象,可以進行組件之間信息共享的橋樑。
在項目裏指定一個繼承自Application的java類 app
併爲兩個Activity都指定爲程序入口
在Main1裏通過輸入文本對app裏面的成員變量dataText進行修改,然後測試Main2中是否能讀取到修改後的dataText
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.shareText);
textView.setText(((App) getApplicationContext()).getTextData());
editText = (EditText) findViewById(R.id.editText);
button = (Button) findViewById(R.id.btnSave);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
((App) getApplicationContext()).setTextData(editText.getText().toString());
}
});
}
運行後發現報錯了
java.lang.ClassCastException: android.app.Application cannot be cast to com.jackie.coursecontext.App
找了一下原因,是AndroidManifest.xml裏面忘記讓當前的Application指向我們自定義的app了
修改一下
<application
android:name=".App"
...
重新運行,Main2中顯示的就是之前輸入的字符串了
重寫一下上面App類繼承自Application的一些方法
onCreate()//創建時執行
onTerminate()//終止時執行
onTrimMemory(int level)
onLowMemory()
onConfigurationChanged(Configuration newConfig)//配置變化時執行
分別給app,Main1,Main2 的週期添加一些Log
10-15 06:36:14.072 24348-24348/com.jackie.coursecontext V/jackie﹕ App Created
10-15 06:36:14.088 24348-24348/com.jackie.coursecontext V/jackie﹕ Main1 Created
10-15 06:42:02.764 26781-26781/com.jackie.coursecontext V/jackie﹕ App Created
10-15 06:42:02.768 26781-26781/com.jackie.coursecontext V/jackie﹕ Main2 Created
可以看到最先執行的是Application的onCreate()
也就是說一些初始化操作放在Application裏面是很好的
程序有的時候不需要與用戶交互,只需要在後臺進行一些動作,這時候就可以用service了
啓動、停止service的時候無論使用1個intent還是2個,操作的都是同一個service。
startService(new Intent(MainActivity.this, MyService.class));
stopService(new Intent(MainActivity.this,MyService.class));
在安卓機上面設置-Apps裏面可以看到正在運行的進程,可以看到一些service。
爲了便於觀察,在自定義service時,重寫一下
onStartCommand(Intent intent, int flags, int startId)
這個方法是在service被啓動時執行
在裏面啓動一個線程,循環執行一個正在運行的語句,以便觀察
new Thread(new Runnable() {
@Override
public void run() {
while(true){
Log.v("jackie","服務正在運行...");
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
10-15 08:50:01.110 2093-3004/com.jackie.courseservice V/jackie﹕ 服務正在運行...
10-15 08:50:01.800 2093-2557/com.jackie.courseservice V/jackie﹕ 服務正在運行...
10-15 08:50:02.120 2093-3004/com.jackie.courseservice V/jackie﹕ 服務正在運行...
10-15 08:50:02.810 2093-2557/com.jackie.courseservice V/jackie﹕ 服務正在運行...
10-15 08:50:03.131 2093-3004/com.jackie.courseservice V/jackie﹕ 服務正在運行...
即使此退出Activity,service也不會停止。
由於這裏按鈕較多,如果每一個按鈕都去寫一個OnClickListener()
那麼會顯得很冗餘。可以讓這個MainActivity**實現OnClickListener接口(非常重要)**,然後重寫當前Activity的OnClick(View v)
通過View的id來區分功能,然後按鈕只要綁定當前Activity就行了。
findViewById(R.id.btnStart).setOnClickListener(this);
findViewById(R.id.btnStop).setOnClickListener(this);
findViewById(R.id.btnBind).setOnClickListener(this);
findViewById(R.id.btnUnBind).setOnClickListener(this);
...
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btnStart:
Log.v("jackie", "開始服務");
startService(new Intent(MainActivity.this, MyService.class));
break;
case R.id.btnStop:
Log.v("jackie","停止服務");
stopService(new Intent(MainActivity.this, MyService.class));
break;
case R.id.btnBind:
Log.v("jackie", "綁定服務");
bindService(new Intent(MainActivity.this, MyService.class), this, BIND_AUTO_CREATE);
break;
case R.id.btnUnBind:
Log.v("jackie", "解綁服務");
unbindService(this);
break;
}
}
如果直接就這樣啓動的話:會報錯
java.lang.RuntimeException: Unable to bind to service
因爲onBind()
方法還沒有進行重寫
@Override
public IBinder onBind(Intent intent) {
return new Binder();
}
點擊綁定服務
10-15 09:20:45.091 15889-15889/com.jackie.courseservice V/jackie﹕ 綁定服務
10-15 09:20:45.131 15889-15889/com.jackie.courseservice V/jackie﹕ Service Connected
啓動並且綁定服務,如果直接停止服務的話,可以看到,Service並沒有destroyed
10-15 09:29:32.380 18152-18152/com.jackie.courseservice V/jackie﹕ 開始服務
10-15 09:29:32.408 18152-18152/com.jackie.courseservice V/jackie﹕ Service created
10-15 09:29:34.393 18152-18152/com.jackie.courseservice V/jackie﹕ 綁定服務
10-15 09:29:34.407 18152-18152/com.jackie.courseservice V/jackie﹕ Service Connected
--------- beginning of system
10-15 09:30:06.144 18152-18152/com.jackie.courseservice V/jackie﹕ 停止服務
必須解綁,纔會destroyed
10-15 09:31:32.793 18152-18152/com.jackie.courseservice V/jackie﹕ 解綁服務
10-15 09:31:32.801 18152-18152/com.jackie.courseservice V/jackie﹕ Service destroyed
如果在已經綁定服務的情況下,直接退出Activity,會報異常
10-15 09:32:52.379 18152-18152/com.jackie.courseservice E/ActivityThread﹕ Activity com.jackie.courseservice.MainActivity has leaked ServiceConnection com.jackie.courseservice.MainActivity@1c2f7e9 that was originally bound here
android.app.ServiceConnectionLeaked: Activity com.jackie.courseservice.MainActivity has leaked ServiceConnection com.jackie.courseservice.MainActivity@1c2f7e9 that was originally bound here
同時,Service會destroy
10-15 09:32:52.384 18152-18152/com.jackie.courseservice V/jackie﹕ Service destroyed
重寫一下onStartCommand(Intent intent, int flags, int startId)
,添加一句Log
點擊啓動服務按鈕:
10-15 09:37:35.279 22880-22880/com.jackie.courseservice V/jackie﹕ 開始服務
10-15 09:37:35.298 22880-22880/com.jackie.courseservice V/jackie﹕ Service created
10-15 09:37:35.298 22880-22880/com.jackie.courseservice V/jackie﹕ Command started
如果重複點擊啓動服務按鈕,可以看到LogCat
10-15 09:37:35.298 22880-22880/com.jackie.courseservice V/jackie﹕ Command started
10-15 09:38:41.231 22880-22880/com.jackie.courseservice V/jackie﹕ 開始服務
10-15 09:38:41.242 22880-22880/com.jackie.courseservice V/jackie﹕ Command started
10-15 09:38:41.650 22880-22880/com.jackie.courseservice V/jackie﹕ 開始服務
10-15 09:38:41.653 22880-22880/com.jackie.courseservice V/jackie﹕ Command started
10-15 09:38:42.061 22880-22880/com.jackie.courseservice V/jackie﹕ 開始服務
10-15 09:38:42.079 22880-22880/com.jackie.courseservice V/jackie﹕ Command started
可見無論怎麼點擊,onCreate()
都只執行一次,而onStartCommand()
則每點擊一次就執行一次。
給MyService添加成員變量private boolean isRunning = false;
並且onCreate()
時isRunning = true;onDestroy()
時isRunning = false;
啓動/停止服務;
10-15 10:01:37.030 711-711/com.jackie.courseservice V/jackie﹕ 開始服務
10-15 10:01:37.034 711-711/com.jackie.courseservice V/jackie﹕ Service created
10-15 10:01:37.034 711-711/com.jackie.courseservice V/jackie﹕ Command started
10-15 10:01:37.044 711-865/com.jackie.courseservice V/jackie﹕ 服務正在運行...
10-15 10:01:39.055 711-865/com.jackie.courseservice V/jackie﹕ 服務正在運行...
10-15 10:01:40.218 711-711/com.jackie.courseservice V/jackie﹕ 停止服務
10-15 10:01:40.223 711-711/com.jackie.courseservice V/jackie﹕ Service destroyed