編輯:關於Android編程
作為 Android四大組件之一, 服務也少不了有很多非常重要的知識點,那自然要從最基本的用法開始學習了。
定義一個服務:
public class MyService extends Service {
/**
* onBind是繼承Service後唯一的一個抽象方法所以必須要重寫的一個方法
*/
@Override
public IBinder onBind(Intent intent) {
return null;
}
/**
* 服務每次啟動的時候調用
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("MyService", "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
/**
* 在服務創建的時候調用
*/
@Override
public void onCreate() {
Log.i("MyService", "onCreate");
super.onCreate();
}
/**
* 會在服務銷毀的時候調用
*/
@Override
public void onDestroy() {
Log.i("MyService", "onDestroy");
super.onDestroy();
}
啟動和停止服務
public class MyServiceActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.androidservice_activity);
Button start=(Button) findViewById(R.id.start);
start.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent(MyServiceActivity.this,MyService.class);
startService(intent);//啟動服務
}
});
Button stop=(Button) findViewById(R.id.stop);
stop.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent(MyServiceActivity.this,MyService.class);
stopService(intent);//停止服務
}
});
}
}
注意,這裡完全是由活動來決定服務何時停止的,如果沒有點擊Stop Service 按鈕, 服務就會一直處於運行狀態。 那服務有沒有什麼辦法讓自已停止下來呢?當然可以, 只需要在 MyService 的任何一個位置調用 stopSelf()方法就能讓這個服務停止下來了。
我們可以看到Logcat打印的信息:
點擊start服務成功啟動,點擊stop然後成功銷毀, 運行成功後可以在正在運行的應用列表當中找到這個應用,停止掉之後就找不到了:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPjxpbWcgYWx0PQ=="這裡寫圖片描述" src="/uploadfile/Collfiles/20160622/20160622091033717.png" title="\" />
已經學會了啟動服務以及停止服務的方法,不知道你心裡現在有沒有一個疑惑,那就是 onCreate()方法和 onStartCommand()到底有什麼區別呢?因為剛剛點擊Start Service按鈕後兩個方法都執行了。其實 onCreate()方法是在服務第一次創建的時候調用的,而 onStartCommand()方法則在每次啟動服務的時候都會調用,由於剛才我們是第一次點擊 Start Service 按鈕,服務此時還未創建過,所以兩個方法都會執行,之後如果你再連續多點擊幾次 Start Service 按鈕,你就會發現只有 onStartCommand()方法可以得到執行了。
活動和服務進行通信
我們在活動裡調用了 startService()方法來啟動 MyService這個服務,然後 MyService的 onCreate()和onStartCommand()方法就會得到執行。之後服務會一直處於運行狀態,但具體運行的是什麼邏輯,活動控制不了了。這就類似於活動通知了服務一下: “你可以啟動了! ”然後服務就去忙自己的事情了,但活動並不知道服務到底去做了什麼事情,以及完成的如何。那麼有沒有什麼辦法能讓活動和服務的關系更緊密一些呢?例如在活動中指揮服務去干什麼,服務就去干什麼。當然可以,這就需要借助我們剛剛忽略的 onBind()方法了。
比如說目前我們希望在 MyService裡提供一個下載功能,然後在活動中可以決定何時開始下載, 以及隨時查看下載進度。 實現這個功能的思路是創建一個專門的 Binder對象來對下載功能進行管理,修改 MyService中的代碼,如下所示:
public class MyService extends Service {
/**
* 創建binder對象
*/
DownLoadBind bind = new DownLoadBind();
class DownLoadBind extends Binder {
/**
* 一個開始下載的方法
*/
public void startDownload() {
Log.i("DownLoadBind", "startDownload");
}
/**
* 一個獲取進度的方法
* @return
*/
public int getProgress() {
Log.i("DownLoadBind", "getProgress");
return 0;
}
}
/**
* onBind是繼承Service後唯一的一個抽象方法所以必須要重寫的一個方法
* 該方法是為了服務可以與服務進行通信,例如在活動中指揮服務去干什麼,服務就去干什麼
* 返回binder對象
*/
@Override
public IBinder onBind(Intent intent) {
return bind;
}
/**
* 服務每次啟動的時候調用
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("MyService", "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
/**
* 在服務創建的時候調用
*/
@Override
public void onCreate() {
Log.i("MyService", "onCreate");
super.onCreate();
}
/**
* 會在服務銷毀的時候調用
*/
@Override
public void onDestroy() {
Log.i("MyService", "onDestroy");
super.onDestroy();
}
接下來看下MyServiceActivity中的代碼,在MyServiceActivity新增了兩個按鈕
public class MyServiceActivity extends Activity{
private MyService.DownLoadBind down;
/**
* 創建ServiceConnection的匿名內部類並重寫兩個方法
*/
private ServiceConnection connection=new ServiceConnection() {
/**
* 在與服務解除綁定的時候調用
*/
@Override
public void onServiceDisconnected(ComponentName name) {
}
/**
* 綁定成功的時候調用
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
down=(DownLoadBind) service;//通過向下轉型得到DownLoadBind的實例有了這個實例活動與服務的關系就變得非常緊密了
down.startDownload();
down.getProgress();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.androidservice_activity);
Button start=(Button) findViewById(R.id.start);
start.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
Intent intent=new Intent(MyServiceActivity.this,MyService.class);
startService(intent);//開始服務
}
});
Button stop=(Button) findViewById(R.id.stop);
stop.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent(MyServiceActivity.this,MyService.class);
stopService(intent);//停止服務
}
});
Button bind=(Button) findViewById(R.id.BindService);
bind.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent(MyServiceActivity.this,MyService.class);
/**
* 第一個參數剛剛構建出的 Intent對象 第二個參數前面創建出的 ServiceConnection的實例,第三個則是一個標志位
* 這裡傳入 BIND_AUTO_CREATE 表示在活動和服務進行綁定後自動創建服務。這會使得
* MyService 中的 onCreate()方法得到執行,但 onStartCommand()方法不會執行。
*/
bindService(intent, connection, BIND_AUTO_CREATE);//綁定服務
}
});
Button unbind=(Button) findViewById(R.id.UnbindService);
unbind.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
unbindService(connection);//解綁服務點擊了就會服務就會停止運行
}
});
}
}
到此為止activity可以調用服務當中任何public方法。實現了在活動當中去控制服務當中的方法,想讓服務干嘛就干嘛。
運行下程序:
Logcat當中可以看到成功運行服務當中的兩個方法以及onCreate方法。另外需要注意,任何一個服務在整個應用程序范圍內都是通用的,即 MyService不僅可以和 MainActivity 綁定,還可以和任何一個其他的活動進行綁定,而且在綁定完成後它們都可以獲取到相同的 DownloadBinder實例。
服務的生命周期
學習過了活動以及碎片的生命周期。類似地,服務也有自己的生命周期,前面我們使用到的
onCreate()、onStartCommand()、onBind()和 onDestroy()等方法都是在服務的生命周期內可能回調的方法。一旦在項目的任何位置調用了 Context 的startService()方法,相應的服務就會啟動起來,並回調 onStartCommand()方法。
如果這個服務之前還沒有創建過,onCreate()方法會先於onStartCommand()方法執行。服務啟動了之後會一直保持運行狀態,直到stopService()或stopSelf()方法被調用。注意雖然每調用一次 startService()方法,onStartCommand()就會執行一次, 但實際上每個服務都只會存在一個實例。
所以不管你調用了多少次 startService()方法,只需調用一次 stopService()或stopSelf()方法,服務就會停止下來了。另外,還可以調用 Context 的bindService()來獲取一個服務的持久連接,這時就會回調服務中的onBind()方法
類似地,如果這個服務之前還沒有創建過,onCreate()方法會先於onBind()方法執行。之後,調用方可以獲取到onBind()方法裡返回的 IBinder對象的實例,這樣就能自由地和服務進行通信了。只要調用方和服務之間的連接沒有斷開,服務就會一直保持運行狀態。當調用了 startService()方法後,又去調用 stopService()方法
這時服務中的 onDestroy()方法就會執行,表示服務已經銷毀了。類似地,當調用了 bindService()方法後,又去調用unbindService()方法,
onDestroy()方法也會執行,這兩種情況都很好理解。但是需要注意,我們是完全有可能對一個服務既調用了 startService()方法,又調用了 bindService()方法的,
這種情況下該如何才能讓服務銷毀掉呢?根據 Android系統的機制,一個服務只要被啟動或 者被綁定了之後,就會一直處於運行狀態,必須要讓以上兩種條件同時不滿足,服務才能被 銷毀。所以,這種情況下要同時調用stopService()和 unbindService()方法,onDestroy()方法才 會執行。
這樣你就已經把服務的生命周期完整地走了一遍。
使用 IntentService
服務中的代碼都是默認運行在主線程當中的,如果直接在服務裡去處理一些耗時的邏輯,就很容易出現 ANR(Application NotResponding)的情況。所以這個時候就需要用到 Android多線程編程的技術了,我們應該在服務的每個具體的方法裡開啟一個子線程,然後在這裡去處理那些耗時的邏輯。因此,一個比較標准的服務就可以寫成如下形式:
public class MyService extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
// 處理具體的邏輯
stopSelf();
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
}
雖說這種寫法並不復雜, 但是總會有一些程序員忘記開啟線程, 或者忘記調用 stopSelf()
方法。為了可以簡單地創建一個異步的、會自動停止的服務,Android 專門提供了一個
IntentService 類,這個類就很好地解決了前面所提到的兩種尴尬,下面我們就來看一下它的
用法。
public class MyIntentService extends IntentService{
/**
* 調用父類的有構造函數
* @param name
*/
public MyIntentService() {
super("MyIntentService");
}
/**
* 這個方法可以去處理一些具體的邏輯問題,不用擔心ARN問題
* 這個方法已經是在子線程中運行的了
*/
@Override
protected void onHandleIntent(Intent intent) {
// 打印當前線程的id
Log.d("MyIntentService", "子線程ID" + Thread.currentThread().getId());
}
/**
* 在創建服務後是會自動停止的
*/
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
Log.i("MyIntentService", "onDestroy");
}
}
在MyServiceActivity新增一個按鈕分別打印子線程ID以及主線程ID
Button startintent=(Button) findViewById(R.id.startintent);
startintent.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Log.i("MyServiceActivity", "主線程ID"+Thread.currentThread().getId());
Intent intent=new Intent(MyServiceActivity.this,MyIntentService.class);
startService(intent);
}
});
運行程序:Logcat打印信息:
可以看到, 不僅 MyIntentService和 MainActivity 所在的線程 id 不一樣, 而且 onDestroy()方法也得到了執行, 說明 MyIntentService在運行完畢後確實自動停止了。 集開啟線程和自動停止於一身IntentService 還是博得了不少程序員的喜愛。
後台執行的定時任務
下面我們就來創建一個可以長期在後台執行定時任務的服務。Android中的定時任務一般有兩種實現方式,一種是使用 Java API 裡提供的 Timer 類,一種是使用 Android的 Alarm機制。我們來看看Alarm機制。
public class LongRunningService extends Service{
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
//打印時間信息
Log.d("LongRunningService", "executed at " + new Date().
toString());
}
}).start();
AlarmManager manager=(AlarmManager) getSystemService(ALARM_SERVICE);
int hour=3000;//每隔三秒刷新一次
/**
* 使用 SystemClock.elapsedRealtime()方法可以獲取到系統開機至今所經歷時間的毫秒數,
* 使用 System.currentTimeMillis()方法可以獲取到 1970年 1 月 1日 0點至今所經歷時間的毫秒數。
*/
long triggerAtTime = SystemClock.elapsedRealtime() + hour;
Intent i = new Intent(this, AlarmReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0);
/**
* set第一個參數整型參數,用於指定 AlarmManager的工作類型,有四種值可選
* ELAPSED_REALTIME、ELAPSED_REALTIME_WAKEUP、RTC 和 RTC_WAKEUP
* ELAPSED_REALTIME: 表示讓定時任務的觸發時間從系統開機開始算起,但不會喚醒 CPU
* ELAPSED_REALTIME_WAKEUP: 同樣表示讓定時任務的觸發時間從系統開機開始算起,但會喚醒 CPU
* RTC:表示讓定時任務的觸發時間從 1970 年 1月 1 日 0點開始算起,但不會喚醒 CPU
* RTC_WAKEUP: 同樣表示讓定時任務的觸發時間從1970 年 1 月 1 日 0 點開始算起,但會喚醒 CPU
*
* 第二個參數表示就是定時任務觸發的時間,以毫秒為單位。
* 如果第一個參數使用的是 ELAPSED_REALTIME或 ELAPSED_REALTIME_WAKEUP,
* 則這裡傳入開機至今的時間再加上延遲執行的時間。如果第一個參數使用的是 RTC 或RTC_WAKEUP,
* 則這裡傳入 1970年 1月 1日 0點至今的時間再加上延遲執行的時間。
*
* 第三個參數是一個 PendingIntent,這裡我們一般會調用 getBroadcast()方法來獲取一個能夠執行廣播的 PendingIntent。
* 這樣當定時任務被觸發的時候,廣播接收器的 onReceive()方法就可以得到執行。
*/
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);
return super.onStartCommand(intent, flags, startId);
}
}
MyServiceActivity:
Button button=(Button) findViewById(R.id.startAlarm);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent(MyServiceActivity.this,LongRunningService.class);
startService(intent);
}
});
AlarmReceiver :
public class AlarmReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Intent intent2=new Intent(context,LongRunningService.class);
context.startService(intent2);
}
}
onReceive()方法裡的代碼非常簡單,就是構建出了一個 Intent 對象,然後去啟動LongRunningService 這個服務。那麼這裡為什麼要這樣寫呢?其實在不知不覺中,這就已經將一個長期在後台定時運行的服務完成了。因為一旦啟動 LongRunningService,就會在onStartCommand()方法裡設定一個定時任務,這樣一小時後 AlarmReceiver 的 onReceive()方法就將得到執行,然後我們在這裡再次啟動 LongRunningService,這樣就形成了一個永久的循環,保證 LongRunningService 可以每隔一段時間就會啟動一次,一個長期在後台定時運行的服務自然也就完成了。
最後別忘了在AndroidManifest.xml注冊
運行:
Logcat打印信息:
可以看到程序已經成功運行了,每隔3秒鐘刷新一次時間。實際運用當中可以把LOG信息換成邏輯即可。
最後來看一個利用服務Service,Handler多線程異步處理機制,HttpURLConnection,寫的一個通知欄版本升級的例子:
public class UpdateService extends Service {
public static final String Install_Apk = "Install_Apk";
/******** download progress step *********/
private static final int down_step_custom = 3;
private static final int TIMEOUT = 10 * 1000;// 超時
private static String down_url;
private static final int DOWN_OK = 1;
private static final int DOWN_ERROR = 0;
private String app_name;
private NotificationManager notificationManager;
private Notification notification;
private Intent updateIntent;
private PendingIntent pendingIntent;
private RemoteViews contentView;
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
/**
* 方法描述:onStartCommand方法
*
* @param Intent
* intent, int flags, int startId
* @return int
* @see UpdateService
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
app_name = intent.getStringExtra("Key_App_Name");
down_url = intent.getStringExtra("Key_Down_Url");
//create file,應該在這個地方加一個返回值的判斷SD卡是否准備好,文件是否創建成功,等等!
FileUtil.createFile(app_name);
if (FileUtil.isCreateFileSucess == true) {
createNotification();
createThread();
} else {
Toast.makeText(this, R.string.insert_card, Toast.LENGTH_SHORT).show();
/*************** stop service ************/
stopSelf();
}
return super.onStartCommand(intent, flags, startId);
}
/********* update UI ******/
private final Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case DOWN_OK:
/********* 下載完成,點擊安裝 ***********/
Uri uri = Uri.fromFile(FileUtil.updateFile);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "application/vnd.android.package-archive");
pendingIntent = PendingIntent.getActivity(UpdateService.this, 0, intent, 0);
notification.flags = Notification.FLAG_AUTO_CANCEL;
notification.setLatestEventInfo(UpdateService.this, app_name, getString(R.string.down_sucess),
pendingIntent);
// notification.setLatestEventInfo(UpdateService.this,app_name,
// app_name + getString(R.string.down_sucess), null);
notificationManager.notify(R.layout.notification_item, notification);
/***** 安裝APK ******/
// installApk();
// stopService(updateIntent);
/*** stop service *****/
stopSelf();
break;
case DOWN_ERROR:
notification.flags = Notification.FLAG_AUTO_CANCEL;
// notification.setLatestEventInfo(UpdateService.this,app_name,
// getString(R.string.down_fail), pendingIntent);
notification.setLatestEventInfo(UpdateService.this, app_name, getString(R.string.down_fail), null);
/*** stop service *****/
// onDestroy();
stopSelf();
break;
default:
// stopService(updateIntent);
/****** Stop service ******/
// stopService(intentname)
// stopSelf();
break;
}
}
};
private void installApk() {
// TODO Auto-generated method stub
/********* 下載完成,點擊安裝 ***********/
Uri uri = Uri.fromFile(FileUtil.updateFile);
Intent intent = new Intent(Intent.ACTION_VIEW);
/**********
* 加這個屬性是因為使用Context的startActivity方法的話,就需要開啟一個新的task
**********/
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(uri, "application/vnd.android.package-archive");
UpdateService.this.startActivity(intent);
}
/**
* 方法描述:createThread方法, 開線程下載
*
* @param
* @return
* @see UpdateService
*/
public void createThread() {
new DownLoadThread().start();
}
private class DownLoadThread extends Thread {
@Override
public void run() {
// TODO Auto-generated method stub
Message message = new Message();
try {
long downloadSize = downloadUpdateFile(down_url, FileUtil.updateFile.toString());
if (downloadSize > 0) {
// down success
message.what = DOWN_OK;
handler.sendMessage(message);
}
} catch (Exception e) {
e.printStackTrace();
message.what = DOWN_ERROR;
handler.sendMessage(message);
}
}
}
/**
* 方法描述:createNotification方法
*
* @param
* @return
* @see UpdateService
*/
public void createNotification() {
/**
* 定義一個前台服務
*/
// notification = new Notification(R.drawable.dot_enable,app_name +
// getString(R.string.is_downing) ,System.currentTimeMillis());
notification = new Notification(
// R.drawable.video_player,//應用的圖標
R.drawable.icon, // 應用的圖標
app_name + getString(R.string.is_downing), System.currentTimeMillis());
notification.flags = Notification.FLAG_ONGOING_EVENT;
// notification.flags = Notification.FLAG_AUTO_CANCEL;
/*** 自定義 Notification 的顯示 ****/
contentView = new RemoteViews(getPackageName(), R.layout.notification_item);
contentView.setTextViewText(R.id.notificationTitle, app_name + getString(R.string.is_downing));
contentView.setTextViewText(R.id.notificationPercent, "0%");
contentView.setProgressBar(R.id.notificationProgress, 100, 0, false);
notification.contentView = contentView;
// updateIntent = new Intent(this, AboutActivity.class);
// updateIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
// //updateIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
// pendingIntent = PendingIntent.getActivity(this, 0, updateIntent, 0);
// notification.contentIntent = pendingIntent;
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(R.layout.notification_item, notification);
}
/***
* down file
*
* @return
* @throws MalformedURLException
*/
public long downloadUpdateFile(String down_url, String file) throws Exception {
int down_step = down_step_custom;// 提示step
int totalSize;// 文件總大小
int downloadCount = 0;// 已經下載好的大小
int updateCount = 0;// 已經上傳的文件大小
InputStream inputStream;
OutputStream outputStream;
URL url = new URL(down_url);
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setConnectTimeout(TIMEOUT);
httpURLConnection.setReadTimeout(TIMEOUT);
// 獲取下載文件的size
totalSize = httpURLConnection.getContentLength();
if (httpURLConnection.getResponseCode() == 404) {
throw new Exception("fail!");
// 這個地方應該加一個下載失敗的處理,但是,因為我們在外面加了一個try---catch,已經處理了Exception,
// 所以不用處理
}
inputStream = httpURLConnection.getInputStream();
outputStream = new FileOutputStream(file, false);// 文件存在則覆蓋掉
byte buffer[] = new byte[1024];
int readsize = 0;
while ((readsize = inputStream.read(buffer)) != -1) {
// /*********如果下載過程中出現錯誤,就彈出錯誤提示,並且把notificationManager取消*********/
// if (httpURLConnection.getResponseCode() == 404) {
// notificationManager.cancel(R.layout.notification_item);
// throw new Exception("fail!");
// //這個地方應該加一個下載失敗的處理,但是,因為我們在外面加了一個try---catch,已經處理了Exception,
// //所以不用處理
// }
outputStream.write(buffer, 0, readsize);
downloadCount += readsize;// 時時獲取下載到的大小
/*** 每次增張3% **/
if (updateCount == 0 || (downloadCount * 100 / totalSize - down_step) >= updateCount) {
updateCount += down_step;
// 改變通知欄
contentView.setTextViewText(R.id.notificationPercent, updateCount + "%");
contentView.setProgressBar(R.id.notificationProgress, 100, updateCount, false);
notification.contentView = contentView;
notificationManager.notify(R.layout.notification_item, notification);
}
}
if (httpURLConnection != null) {
httpURLConnection.disconnect();
}
inputStream.close();
outputStream.close();
return downloadCount;
}
}
public class FileUtil {
public static File updateDir = null;
public static File updateFile = null;
/***********保存升級APK的目錄***********/
public static final String KonkaApplication = "konkaUpdateApplication";
public static boolean isCreateFileSucess;
/**
* 方法描述:createFile方法
* @param String app_name
* @return
* @see FileUtil
*/
public static void createFile(String app_name) {
if (android.os.Environment.MEDIA_MOUNTED.equals(android.os.Environment.getExternalStorageState())) {
isCreateFileSucess = true;
updateDir = new File(Environment.getExternalStorageDirectory()+ "/" + KonkaApplication +"/");
updateFile = new File(updateDir + "/" + app_name + ".apk");
if (!updateDir.exists()) {
updateDir.mkdirs();
}
if (!updateFile.exists()) {
try {
updateFile.createNewFile();
} catch (IOException e) {
isCreateFileSucess = false;
e.printStackTrace();
}
}
}else{
isCreateFileSucess = false;
}
}
}
Activity:
// 點擊更新
update.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(context,UpdateService.class);
intent.putExtra("Key_App_Name",appName);
intent.putExtra("Key_Down_Url",appUrl);
startService(intent);
popupWindow.dismiss();
}
});
我們在做Android應用尤其是商業應用的時候,很多時候都需要後期版本升級,如果我們的數據庫文件非常大,比如游戲之類的,這時候就不應該每次版本更新都去重新復制數據庫。將數
現在在手上的是一個證券資訊類型的app,其中有涉及到股票行情界面,行情中有K線圖等,看到網上很多人在求這方面的資料,所以我特地寫了一個demo在此處給大家分享一下。下面是
最近學習了一個視頻公開課,講到了利用HorizontalScrollView仿ViewPager設計的一個簡單相冊,其實主要用了ViewPager緩存的思想。此篇文章參考
問題的起源在項目裡,有時候需要實現一個圖片輪播的效果,用來展示Banner。同時,圖片能循環播放,下面還有一排小圓點來指示當前輪播到哪一頁了。如下圖:分析· 圖片的個數是