1.首先无SD卡如何存储?
我们将下载的文件放入 /data/data/packageName/files/ 目录下
public class FileUtil {
public static File getFileHasNoSDCard(Context context, String fileName){
boolean flag = false;
/**
* 返回一个目录 /data/data/packageName/files/
* 之所以新建一个文件夹存放apk文件,是因为,如果直接新建/data/data/packageName/files/test.apk
* 可能会出现 open failed: EISDIR (Is a directory)
*/
File dir = new File(context.getFilesDir() + "/myapk");
File file = new File(dir, fileName);
if (!dir.exists()){
dir.mkdirs();
}
file.delete();
if (!file.exists()){
try {
flag = file.createNewFile();
String command1 = "chmod 775 " + dir.getAbsolutePath();
String command2 = "chmod 777 " + file.getAbsolutePath();
Runtime runtime = Runtime.getRuntime();
runtime.exec("su");
runtime.exec(command1);
runtime.exec(command2);
}catch (Exception e){
e.printStackTrace();
}
}
return flag ? file : null;
}
}
2.下面是下载的2中方式,一种是弹出progressDialog,另一种是notification
第一种:
public class UpdateByProgressDialog {
private MainActivity instance;
private ProgressDialog progressDialog;
private File newFile;
private String downUrl;
private String appName;
public UpdateByProgressDialog(MainActivity instance, String downUrl, String appName){
this.instance = instance;
this.downUrl = downUrl;
this.appName = appName;
}
public void downFileByProgressDialog(){
progressDialog = new ProgressDialog(instance);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
//false 设置进度条进度不确定
progressDialog.setIndeterminate(false);
progressDialog.setTitle(instance.getString(R.string.is_downloading));
progressDialog.setMessage(instance.getString(R.string.please_wait));
progressDialog.setCanceledOnTouchOutside(false);
progressDialog.show();
//启动线程
MyRunnable myRunnable = new MyRunnable();
new Thread(myRunnable).start();
}
class MyRunnable implements Runnable{
@Override
public void run() {
HttpClient httpClient = new DefaultHttpClient();
HttpGet get = new HttpGet(downUrl);
HttpResponse response;
InputStream is = null;
FileOutputStream fos = null;
try {
response = httpClient.execute(get);
HttpEntity httpEntity = response.getEntity();
long length = httpEntity.getContentLength();
if (length == -1 || length == 0){
Toast.makeText(instance, "文件不存在", Toast.LENGTH_SHORT).show();
return;
}
//进度最大值设为100
progressDialog.setMax(100);
progressDialog.setProgress(0);
is = httpEntity.getContent();
if (is != null){
newFile = FileUtil.getFileHasNoSDCard(instance, appName);
if (newFile == null){
Toast.makeText(instance, "内置SD卡异常", Toast.LENGTH_SHORT).show();
return;
}
fos = new FileOutputStream(newFile);
byte[] buffer = new byte[1024];
int sum = 0;
int count = -1;
while ((count = is.read(buffer)) != -1){
fos.write(buffer, 0 , count);
sum += count;
if (length > 0){
int progress = (int)(sum * 100 / length);
progressDialog.setProgress(progress);
}
}
fos.flush();
handler.sendEmptyMessage(0);
}
} catch (IOException e) {
e.printStackTrace();
}
finally {
if (progressDialog != null && progressDialog.isShowing()){
progressDialog.dismiss();
}
if (fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (is != null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
//设置最后进度100
progressDialog.setProgress(100);
//安装程序
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(newFile), "application/vnd.android.package-archive");
instance.startActivity(intent);
}
};
}
第二种:
public class UpdateByNotificationService extends Service {
private String appName;
private String downUrl;
private File newFile;
private static final int TIMEOUT = 10 * 1000;
private NotificationManager notificationManager;
private Notification notification;
//remoteView 字面理解远程的视图,描述一个view对象能够显示在其他进程中,可以融合一个layout资源文件实现布局
private RemoteViews contentView;
private PendingIntent pendingIntent;
@Override
public void onCreate() {
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
appName = intent.getStringExtra("appName");
downUrl = intent.getStringExtra("downUrl");
Log.d("appName", appName + downUrl);
newFile = FileUtil.getFileHasNoSDCard(getApplicationContext(), appName);
if (newFile != null){
createNotification();
//启动下载线程
new DownloadThread().start();
}else {
Toast.makeText(getApplicationContext(), "内置SD卡异常", Toast.LENGTH_SHORT).show();
stopSelf();
}
return super.onStartCommand(intent, flags, startId);
}
/**
* 创建通知
*/
private void createNotification(){
notification = new Notification(R.drawable.header, appName + getString(R.string.is_downloading), System.currentTimeMillis());
notification.flags = Notification.FLAG_ONGOING_EVENT;
//自定义notification的显示
contentView = new RemoteViews(getPackageName(), R.layout.notification_item);
contentView.setTextViewText(R.id.notificationTitle, appName + getString(R.string.is_downloading));
contentView.setTextViewText(R.id.notificationPercent, "0%");
contentView.setProgressBar(R.id.notificationProgress, 100, 0, false);
notification.contentView = contentView;
notification.contentIntent = pendingIntent;
notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(R.layout.notification_item, notification);
}
private class DownloadThread extends Thread{
@Override
public void run() {
super.run();
long downloadSize = downloadUpdateFile();
if (downloadSize > 0){
mHandler.sendEmptyMessage(0);
}else {
mHandler.sendEmptyMessage(1);
}
}
}
/**
* 下载更新的文件
*/
public long downloadUpdateFile(){
int down_step = 3;//每次更新的进度数
int total_size = 0;//文件总大小
int downloadCount = 0;//已经下载好的大小
int updateCount = 0;//已上传的文件大小
InputStream is = null;
FileOutputStream fos = null;
HttpURLConnection httpURLConnection = null;
try {
URL url = new URL(downUrl);
httpURLConnection = (HttpURLConnection)url.openConnection();
httpURLConnection.setConnectTimeout(TIMEOUT);
httpURLConnection.setReadTimeout(TIMEOUT);
//获取下载文件的size
total_size = httpURLConnection.getContentLength();
if (httpURLConnection.getResponseCode() == 404){
return 0;
}
is = httpURLConnection.getInputStream();
fos = new FileOutputStream(newFile, false);//文件存在则覆盖
byte[] buffer = new byte[1024];
int readSize = -1;
// contentView = new RemoteViews(getPackageName(), R.layout.notification_item);
while ((readSize = is.read(buffer)) != -1){
fos.write(buffer, 0 ,readSize);
downloadCount += readSize;//实时获取下载的大小
//看着是匀速下载,其实不一定,这里只是超过了才更新它
if (updateCount == 0 || (downloadCount * 100 / total_size - down_step) >= updateCount){
updateCount += down_step;
//改变通知栏
contentView = new RemoteViews(getPackageName(), R.layout.notification_item);
contentView.setTextViewText(R.id.notificationTitle, updateCount + "%");
contentView.setTextViewText(R.id.notificationPercent, updateCount + "%");
contentView.setProgressBar(R.id.notificationProgress, 100, updateCount, false);
notification.contentView = contentView;
notification.contentIntent = pendingIntent;
notificationManager.notify(R.layout.notification_item, notification);
}
}
}
catch (MalformedURLException e){
e.printStackTrace();
}
catch(IOException e) {
e.printStackTrace();
}
finally {
if (httpURLConnection != null){
httpURLConnection.disconnect();
}
if (fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (is != null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return downloadCount;
}
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 0:
Uri uri = Uri.fromFile(newFile);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "application/vnd.android.package-archive");
//pendingIntent与intent的区别是,pendingIntent的执行不是立刻的,实质是参数传进来的操作
//pendingIntent的目的在于它所包含的intent的操作是满足某些条件的,通常用于notification的发送,
// 短消息SMSManager的发送和警报器AlarmManager的执行
//intent一般是activity,BroadcastReceiver,service之间传递数据,而pendingIntent,一般用在notification上
//可以理解为延时的intent,pendingIntent是对Intent的一个包装
pendingIntent = PendingIntent.getActivity(UpdateByNotificationService.this, 0, intent, 0);
notification.flags = Notification.FLAG_AUTO_CANCEL;
notification.setLatestEventInfo(UpdateByNotificationService.this, appName, getString(R.string.download_success), pendingIntent);//点击后将会执行pendingIntent
notificationManager.notify(R.layout.notification_item, notification);
stopSelf();
break;
case 1:
notification.flags = Notification.FLAG_AUTO_CANCEL;
notification.setLatestEventInfo(UpdateByNotificationService.this, appName, getString(R.string.download_fail), null);
stopSelf();
break;
}
}
};
}
下面是MainActivity类
/**
* 本例是一个下载app的事例
* 分2中,一种用ProgressDialog进度条,另一种用Notification通知栏
* 本例着重 在手机无SD卡时候,不得不下载到/data/data/packageName/files/ 目录下
*/
public class MainActivity extends Activity implements View.OnClickListener{
public static final String downUrl = "http://www.apk3.com/uploads/soft/201504/jcjx.045703.apk";
// public static final String downUrl = "http://down.360safe.com/instmobilemgr.exe";//这不是坑爹吗,有些链接下载下来不能安装,估计是链接问题
private Button btn_progressDialog, btn_notification;
private MainActivity instance;
private static final String appName = "test.apk";
private UpdateByProgressDialog updateByProgressDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
instance = this;
btn_progressDialog = (Button)findViewById(R.id.btn_progressDialog);
btn_notification = (Button)findViewById(R.id.btn_notification);
btn_progressDialog.setOnClickListener(this);
btn_notification.setOnClickListener(this);
updateByProgressDialog = new UpdateByProgressDialog(instance, downUrl, appName);
}
@Override
public void onClick(View view) {
switch (view.getId()){
//方法一:弹出progressDialog
case R.id.btn_progressDialog:
updateByProgressDialog.downFileByProgressDialog();
break;
//方法二:弹出notification
//方法二的实现在UpdataService.class
case R.id.btn_notification:
Intent intent = new Intent(instance, UpdateByNotificationService.class);
intent.putExtra("appName",appName);
intent.putExtra("downUrl",downUrl);
startService(intent);
break;
}
}
}
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="30sp"
android:layout_centerHorizontal="true"
android:layout_marginTop="40dp"
android:text="下载apk的2种方式"/>
<LinearLayout
android:layout_centerInParent="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button
android:id="@+id/btn_progressDialog"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="方法一"/>
<Button
android:id="@+id/btn_notification"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="方法二"/>
</LinearLayout>
</RelativeLayout>
下面是通知的布局文件notification_item.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="30sp"
android:layout_centerHorizontal="true"
android:layout_marginTop="40dp"
android:text="下载apk的2种方式"/>
<LinearLayout
android:layout_centerInParent="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button
android:id="@+id/btn_progressDialog"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="方法一"/>
<Button
android:id="@+id/btn_notification"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="方法二"/>
</LinearLayout>
</RelativeLayout>
下面是写的过程中遇到的问题:
java.io.FileNotFoundException: /data/data/com.suhu_tech.downloadapp/files/test.apk: open failed: EISDIR (Is a directory)
原因:直接新建程序会认为是个文件夹而不是个文件,在test.apk上面再加一层即可
参考自:
http://blog.csdn.net/zhufuing/article/details/8666230
http://blog.csdn.net/dalancon/article/details/38111679