項目中要使用到版本更新的功能,參考了開源中國源碼,也遇到一些問題,再次記錄一下:
遇到的問題:
1. 1.notifacation.bulider怎麼使用?
2. notifacation和notifacation.bulider有什麼區別?
3. 文件創建遇到的坑。
4. bind和service傳值。
5. 退出時要注意的事項。
step1:
開始需要比對服務器的apk版本。
json結構是這樣的
{"versionCode":"1","vsesionContent":"更新apk"}
查看本身的apk版本:
versionCode = Application.getContext().getPackageManager().getPackageInfo(packageName, 0).versionCode;
step2:
如果服務器版本號大於當前版本號開始下載。
在這裏先說明service有兩種啓動方式:
第一種:startService(Intent);
第二種:bindService(Intent,ServiceConnection,flags);
兩種方式關閉不一樣,需要單獨關閉纔可以。
第一種:
Intent intent=new Intent(content,DownloadService.class);
content.startService();
第二種如果需要和activity交互數據的話可以使用。
以下是我寫的啓動方式
public static void openDownLoadService(Context context, String downurl,
String tilte) {
final ICallbackResult callback = new ICallbackResult() {
@Override
public void OnBackResult(Object s) {
}
};
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
DownloadService.DownloadBinder binder = (DownloadService.DownloadBinder) service;
binder.addCallback(callback);
binder.start();
}
};
Intent intent = new Intent(context, DownloadService.class);
intent.putExtra(DownloadService.BUNDLE_DOWNLOAD_URL, downurl);
intent.putExtra(DownloadService.BUNDLE_KEY_TITLE, tilte);
context.startService(intent);
context.bindService(intent, conn, Context.BIND_AUTO_CREATE);
}
我自己寫了一個接口,從onServiceConnected獲取service返回的數據。
service 執行的順序是:
oncreate->onStartCommand->Binder->onServiceConnected
在OnCreate進行一些初始化的數據。
比如:實例化 DownloadBinder,以及NotificationManager
在onServiceConnected判斷是否已經可以對activity進行通訊了
如果已經連接上了就開啓線程來下載apk文件
DownloadService.DownloadBinder binder = (DownloadService.DownloadBinder) service;
binder.addCallback(callback);
binder.start();
Log.d("aa", "onServiceConnected");
接下來放出 DownloadBinderBinder類
public class DownloadBinder extends Binder {
public void start() {
Log.d("aa","Binder");
if (downLoadThread == null || !downLoadThread.isAlive()) {
progress = 0;
**1**.setUpNotification();
new Thread() {
public void run() {
// 下載
**2** startDownload();
}
}.start();
}
}
public void cancel() {
canceled = true;
}
public int getProgress() {
return progress;
}
public boolean isCanceled() {
return canceled;
}
public boolean serviceIsDestroy() {
return serviceIsDestroy;
}
public void cancelNotification() {
mHandler.sendEmptyMessage(2);
}
public void addCallback(ICallbackResult callback) {
DownloadService.this.callback = callback;
}
}
先看到 1:
因爲我們要在通知欄自定義下載的進度,所有我們要自己寫入佈局.
那麼我們會使用到該類 NotificationCompat.Builder
爲什麼不直接使用 Notification 是因爲有些方法已經hide了,比如 這個setLatestEventInfo();
setLatestEventInfo()方法來爲通知初始化佈局和數據。
接下來是我寫的:
/**
* 設置notification
*/
private void setUpNotification() {
CharSequence tickerText = "準備下載";
long when = System.currentTimeMillis();
NotificationBuilder = new NotificationCompat.Builder(mContext);
/**
* 這個屬性一定要加.不然顯示不了
*/
NotificationBuilder.setSmallIcon(R.mipmap.logoicon);
NotificationBuilder.setWhen(when);
NotificationBuilder.setTicker(tickerText);
NotificationBuilder.setOngoing(true);
/**
* 自定義試圖
*/
contentView = new RemoteViews(getPackageName(), R.layout.notification_download_show);
contentView.setTextViewText(R.id.tv_download_state, mTitle);
NotificationBuilder.setContent(contentView);
/**
* 設置隱身跳轉----
*/
// Intent intent = new Intent(this, MainActivity.class);
// PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
// intent, PendingIntent.FLAG_UPDATE_CURRENT);
// NotificationBuilder.setContentIntent(contentIntent);
mNotificationManager.notify(NOTIFY_ID, NotificationBuilder.build());
}
詳細的資料可以參考
然後看到 2
前提:因爲service是運行在主線程中所以我們在service開啓線程進行下載apk操作,然後在通過安裝apk 進行更新
這裏就需要設計到一個文件創建的問題了, 因爲我們再次安裝下載好的apk時需要一個制定下載路徑。
看到這個方法
public static void installAPK(Context context, File file) {
if (file == null || !file.exists())
return;
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file),
"application/vnd.android.package-archive");
context.startActivity(intent);
}
這裏需要File 這個file就是我們下載apk的地方。也是因爲文件這個問題,花費了我幾個小時。
原來的代碼:
File file = new File(AppConfig.DEFAULT_SAVE_IMAGE_PATH);
if (!file.exists()) {
file.mkdir();
}
String apkFile = saveFileName;
File saveFile = new File(apkFile);
現在代碼
File file = new File(AppConfig.DEFAULT_SAVE_IMAGE_PATH);
if (!file.exists()) {
file.mkdirs();
}
String apkFile = saveFileName;
File saveFile = new File(apkFile);
有沒有看出來有什麼不同。
file.mkdirs(); file.mkdir(); 這個地方少些一個s。我心都涼了
第一個函數 如果是 xxx/load/xxx.apk 是可以創建的。支持多文件創建
第二個函數,只支持一個文件一個文件創建。
如果文件創建對了,就進行網絡下載apk的操作了
代碼如下:
/**
* 下載文件
*
* @param downloadUrl
* @param saveFile
* @return
* @throws IOException
*/
private long downloadUpdateFile(String downloadUrl, File saveFile) throws IOException {
int downloadCount = 0;
int currentSize = 0;
long totalSize = 0;
int updateTotalSize = 0;
HttpURLConnection httpConnection = null;
InputStream is = null;
FileOutputStream fos = null;
try {
URL url = new URL(downloadUrl);
httpConnection = (HttpURLConnection) url.openConnection();
httpConnection
.setRequestProperty("User-Agent", "PacificHttpClient");
if (currentSize > 0) {
httpConnection.setRequestProperty("RANGE", "bytes="
+ currentSize + "-");
}
httpConnection.setConnectTimeout(10000);
httpConnection.setReadTimeout(20000);
httpConnection.setRequestMethod("GET");
updateTotalSize = httpConnection.getContentLength();
if (httpConnection.getResponseCode() == 404) {
throw new Exception("fail!");
}
is = httpConnection.getInputStream();
fos = new FileOutputStream(saveFile, false);
byte buffer[] = new byte[1024];
int readsize = 0;
while ((readsize = is.read(buffer)) > 0) {
fos.write(buffer, 0, readsize);
totalSize += readsize;
// 爲了防止頻繁的通知導致應用吃緊,百分比增加10才通知一次
if ((downloadCount == 0)
|| (int) (totalSize * 100 / updateTotalSize) - 10 >= downloadCount) {
downloadCount += 10;
// 更新進度
Message msg = mHandler.obtainMessage();
msg.what = 1;
msg.arg1 = downloadCount;
mHandler.sendMessage(msg);
if (callback != null)
callback.OnBackResult(progress);
}
}
// 下載完成通知安裝
mHandler.sendEmptyMessage(0);
// 下載完了,cancelled也要設置
canceled = true;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (httpConnection != null) {
httpConnection.disconnect();
}
if (is != null) {
is.close();
}
if (fos != null) {
fos.close();
}
}
return totalSize;
}
我這裏使用到的是原始的網絡請求 配合handler進行通知欄的更新
private Handler mHandler = new Handler() {
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
switch (msg.what) {
case 0:
// 下載完畢
mNotificationManager.cancel(NOTIFY_ID);
installApk();
break;
case 2:
// 取消通知
mNotificationManager.cancel(NOTIFY_ID);
break;
case 1:
int rate = msg.arg1;
if (rate < 100) {
contentView.setTextViewText(R.id.tv_download_state, mTitle + "(" + rate
+ "%" + ")");
contentView.setProgressBar(R.id.pb_download, 100, rate,
false);
} else {
// 下載完畢後變換通知形式
NotificationBuilder.setAutoCancel(true);
serviceIsDestroy = true;
stopSelf();// 停掉服務自身
}
mNotificationManager.notify(NOTIFY_ID, NotificationBuilder.build());
break;
}
}
};
對了 最後還需要注意一個問題:
就是程序如果是 System.exit(0); 這樣退出的話,那麼通知欄就會在下載了 service也會終止掉
效果就是這樣
參考
Android Service完全解析,關於服務你所需知道的一切(下)
Android 狀態欄通知Notification詳解
開源中國app源碼