android APP实现更新 PHP后台服务器

简介: android APP实现更新 PHP后台服务器 最近自己在做一款APP,需要实现APP版本更新功能,从网上找了许多资料,只找到了关于移动端的实现。经过我的研究,终于实现了比较完整的android APP版本更新功能,在此分享给广大朋友,但是我的ios端还没实现,但是传输是基于的http协议,实现原理应该是大同小异的。

android APP实现更新 PHP后台服务器

最近自己在做一款APP,需要实现APP版本更新功能,从网上找了许多资料,只找到了关于移动端的实现。经过我的研究,终于实现了比较完整的android APP版本更新功能,在此分享给广大朋友,但是我的ios端还没实现,但是传输是基于的http协议,实现原理应该是大同小异的。接下来进入正文。

PHP后台服务器实现

后台的实现是基于的thinkPHP框架。对于PHP开发框架thinkPHP框架的朋友,只要去官网下载手册,看那么几页,我想你应该就懂了。

接下来直接给出PHP代码实现。

对于这个文件,大家需要修改的便是命名空间。

namespace API\Controller;
use Think\Controller;

至于为什么,大家需要了解下PHP的基础知识,和thinkPHP框架的开发流程就可以了,很快的。

<?php
namespace API\Controller;
use Think\Controller;
class ServerController extends Controller {
    public function index(){
        //$this->show('<style type="text/css">*{ padding: 0; margin: 0; } div{ padding: 4px 48px;} body{ background: #fff; font-family: "寰蒋闆呴粦"; color: #333;font-size:24px} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.8em; font-size: 36px }</style><div style="padding: 24px 48px;"> <h1>:)</h1><p>娆㈣繋浣跨敤 <b>ThinkPHP</b>锛?/p><br/>[ 鎮ㄧ幇鍦ㄨ闂殑鏄疕ome妯″潡鐨処ndex鎺у埗鍣?]</div><script type="text/javascript" src="http://tajs.qq.com/stats?sId=9347272" charset="UTF-8"></script>','utf-8');
        echo "hello";
    }

    function updateInfo(){
        $result=array(
            'version'=>'1.0',
            'description'=>'目前已经更新了卡屏功能',
            'url'=>'http://localhost/index.php/API/Server/download'
        );
        echo json_encode($result);
    }

    function download(){
        $file = "/tmp/test.apk";

        $filename = basename($file);//返回路径中的文件名部分。
        /*
         * header() 函数向客户端发送原始的 HTTP 报头。
         * 必须在任何实际的输出被发送之前调用 header() 函数
         * */
        header("Content-type: application/octet-stream");

//处理中文文件名
        $ua = $_SERVER["HTTP_USER_AGENT"];
        $encoded_filename = rawurlencode($filename);
        if (preg_match("/MSIE/", $ua)) {
            header('Content-Disposition: attachment; filename="' . $encoded_filename . '"');
        } else if (preg_match("/Firefox/", $ua)) {
            header("Content-Disposition: attachment; filename*=\"utf8''" . $filename . '"');
        } else {
            header('Content-Disposition: attachment; filename="' . $filename . '"');
        }

        header("Content-Length: ". filesize($file));

        /*
         * readfile() 函数输出一个文件。
         * 该函数读入一个文件并写入到输出缓冲。
         * 若成功,则返回从文件中读入的字节数。若失败,则返回 false。
         */
        ob_clean();
        flush();
        readfile($file);
    }
}



 

 
 

这里需要解释的是

function updateInfo()
这个方法实现的是获得最近的版本信息。然后和移动端进行匹配
URL地址是:http://localhost/index.php/API/Server/updateinfo,大家可以根据自己的服务器或者云服务器的IP地址进行修改。
 
function download(){
这个方法实现的是下载功能,也就是核心方法。移动端需要访问到这个IP地址才能进行文件的下载。
URL地址是:http://localhost/index.php/API/Server/download,大家可以根据自己的服务器或者云服务器的IP地址进行修改。
 
关于需要下载的文件路径,我是存放在ubuntu系统的/tmp目录下的
 
android移动端实现
 
移动端我直接给出了实现放的类和方法以及活动,就不解释了。
只给出基本的原理
1、从这个URL地址http://localhost/index.php/API/Server/updateinfo获取最新的版本信息,通过http协议和json协议解析出基本的版本信息,然后存储在UpdateInfo类中,然后和APP的当前版本进行比较,如果有最新版则下载。
2、关于下载,android端的核心实现代码是
new Thread() {
         public void run() {        
            HttpClient client = new DefaultHttpClient();
            HttpGet get = new HttpGet(url);
            HttpResponse response;
            try {
               response = client.execute(get);
               HttpEntity entity = response.getEntity();
               int length = (int) entity.getContentLength();
                                        progressDialog.setMax(length);
               InputStream is = entity.getContent();
               FileOutputStream fileOutputStream = null;
               if (is != null) {
                  File file = new File(
                        Environment.getExternalStorageDirectory(),
                        "Test.apk");
                  fileOutputStream = new FileOutputStream(file);
                        byte[] buf = new byte[10];   
                  int ch = -1;
                  int process = 0;
                  while ((ch = is.read(buf)) != -1) {       
                     fileOutputStream.write(buf, 0, ch);
                     process += ch;
                     progressDialog.setProgress(process);
                  }
               }
               fileOutputStream.flush();
               if (fileOutputStream != null) {
                  fileOutputStream.close();
               }
               down();
            } catch (ClientProtocolException e) {
               e.printStackTrace();
            } catch (IOException e) {
               e.printStackTrace();
            }
         }

      }.start();

而PHP的实现代码是download中的

ob_clean();
flush();
readfile($file);

至于底层的原理是,readfile方法封装了,http文件传输的基本实现,大家可以百度下,以上两段代码配合,一个客户端,一个服务器,就能够实现基于http文件的传输.

 
最后给出android端的代码
 
 
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

import com.example.administrator.dongzhiwuapp.R;

public class SetActivity extends Activity {

   private UpdateInfo info;
   private ProgressDialog progressDialog;//下载进度条窗口
   UpdateInfoService updateInfoService;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.update);
        Button button=(Button)findViewById(R.id.button1);
       button.setOnClickListener(new OnClickListener() {
         @Override
         public void onClick(View v) {
            checkUpdate();
         }
      });
   }



   private void checkUpdate(){
      new Thread() {
         public void run() {
            try {
               updateInfoService = new UpdateInfoService(SetActivity.this);
               info = updateInfoService.getUpDateInfo();
               handler1.sendEmptyMessage(0);
            } catch (Exception e) {
               e.printStackTrace();
            }
         };
      }.start();
   }
   
   @SuppressLint("HandlerLeak")
   private Handler handler1 = new Handler() {
      public void handleMessage(Message msg) {
         if (updateInfoService.isNeedUpdate()) {
            showUpdateDialog();
         }
      }
   };

   private void showUpdateDialog() {
      AlertDialog.Builder builder = new AlertDialog.Builder(this);
      builder.setIcon(android.R.drawable.ic_dialog_info);
      builder.setTitle("请升级APP版本至" + info.getVersion());
      builder.setMessage(info.getDescription());
      builder.setCancelable(false);
      builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
         @Override
         public void onClick(DialogInterface dialog, int which) {
            if (Environment.getExternalStorageState().equals(
                  Environment.MEDIA_MOUNTED)) {
               downFile(info.getUrl());
            } else {
               Toast.makeText(SetActivity.this, "SD卡不可用,请插入SD卡",
                     Toast.LENGTH_SHORT).show();
            }
         }
      });
      builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
         @Override
         public void onClick(DialogInterface dialog, int which) {
         }
      });
      builder.create().show();
   }


   //进入下载
   void downFile(final String url) { 
      progressDialog = new ProgressDialog(SetActivity.this);
      progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
      progressDialog.setTitle("正在下载");
      progressDialog.setMessage("请稍后...");
      progressDialog.setProgress(0);
      progressDialog.show();
      updateInfoService.downLoadFile(url, progressDialog,handler1);
   }
   
}
 
 
 
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

import com.example.administrator.dongzhiwuapp.R;

public class SetActivity extends Activity {

   private UpdateInfo info;
   private ProgressDialog progressDialog;//下载进度条窗口
   UpdateInfoService updateInfoService;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.update);
        Button button=(Button)findViewById(R.id.button1);
       button.setOnClickListener(new OnClickListener() {
         @Override
         public void onClick(View v) {
            checkUpdate();
         }
      });
   }



   private void checkUpdate(){
      new Thread() {
         public void run() {
            try {
               updateInfoService = new UpdateInfoService(SetActivity.this);
               info = updateInfoService.getUpDateInfo();
               handler1.sendEmptyMessage(0);
            } catch (Exception e) {
               e.printStackTrace();
            }
         };
      }.start();
   }
   
   @SuppressLint("HandlerLeak")
   private Handler handler1 = new Handler() {
      public void handleMessage(Message msg) {
         if (updateInfoService.isNeedUpdate()) {
            showUpdateDialog();
         }
      }
   };

   private void showUpdateDialog() {
      AlertDialog.Builder builder = new AlertDialog.Builder(this);
      builder.setIcon(android.R.drawable.ic_dialog_info);
      builder.setTitle("请升级APP版本至" + info.getVersion());
      builder.setMessage(info.getDescription());
      builder.setCancelable(false);
      builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
         @Override
         public void onClick(DialogInterface dialog, int which) {
            if (Environment.getExternalStorageState().equals(
                  Environment.MEDIA_MOUNTED)) {
               downFile(info.getUrl());
            } else {
               Toast.makeText(SetActivity.this, "SD卡不可用,请插入SD卡",
                     Toast.LENGTH_SHORT).show();
            }
         }
      });
      builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
         @Override
         public void onClick(DialogInterface dialog, int which) {
         }
      });
      builder.create().show();
   }


   //进入下载
   void downFile(final String url) { 
      progressDialog = new ProgressDialog(SetActivity.this);
      progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
      progressDialog.setTitle("正在下载");
      progressDialog.setMessage("请稍后...");
      progressDialog.setProgress(0);
      progressDialog.show();
      updateInfoService.downLoadFile(url, progressDialog,handler1);
   }
   
}
/**
 * 获取服务器地址
 */

public class GetServerUrl{
   static String url="http://";
         
   public static String getUrl() {
      return url;
   }
}
/*
*
*  版本更新信息类
*  1、获取版本字符串信息
*  2、设置版本字符串信息
*  3、获取版本描述
*  4、设置版本描述
*  5、获取URL地址
*  6、设置URL地址
* */

public class UpdateInfo
{
        private String version;
        private String description;
        private String url;
        
        public String getVersion()
        {
                return version;
        }
        public void setVersion(String version)
        {
                this.version = version;
        }
        public String getDescription()
        {
                return description;
        }
        public void setDescription(String description)
        {
                this.description = description;
        }
        public String getUrl()
        {
                return url;
        }
        public void setUrl(String url)
        {
                this.url = url;
        }
        
}
 
 
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

public class UpdateInfoService {
   ProgressDialog progressDialog;
   Handler handler;
   Context context;
   UpdateInfo updateInfo;
   
   public UpdateInfoService(Context context){
      this.context=context;
   }


   /*
   * 获取更新信息
   * 版本信息存储在服务器的update.txt文件中
   * 更新信息包括:
   * 1、版本信息
   * 2、最新版本具体描述
   * 3、下载地址
   *
   * */
   public UpdateInfo getUpDateInfo() throws Exception {
      HttpClient httpClient = new DefaultHttpClient();
      HttpGet httpGet = new HttpGet("http://localhost/index.php/API/Server/updateInfo");

      HttpResponse httpResponse = httpClient.execute(httpGet);
      UpdateInfo updateInfo = new UpdateInfo();
      if (httpResponse.getStatusLine().getStatusCode() == 200)//请求相应成功
      {
         HttpEntity entity = httpResponse.getEntity();
         String response = EntityUtils.toString(entity, "utf-8");
         JSONObject jsonObject=new JSONObject(response);

         Log.d("dsdsada", "getUpDateInfo: "+response);
         updateInfo.setVersion(jsonObject.getString("version"));
         updateInfo.setDescription(jsonObject.getString("description"));
         updateInfo.setUrl(jsonObject.getString("url"));
         this.updateInfo = updateInfo;
         return updateInfo;
      }
      updateInfo.setVersion("");
      updateInfo.setDescription("");
      updateInfo.setUrl("");
      this.updateInfo = updateInfo;

      return updateInfo;
   }

   /*
    *判断是否需要更新
    */
   public boolean isNeedUpdate(){
         String new_version = updateInfo.getVersion();
         String now_version="";
         try {
            PackageManager packageManager = context.getPackageManager();
            PackageInfo packageInfo = packageManager.getPackageInfo(
                  context.getPackageName(), 0);
            now_version= packageInfo.versionName;
         } catch (NameNotFoundException e) {
            e.printStackTrace();
         }
         if (new_version.equals(now_version)) {
            return false;
         } else {
            return true;
         }
   }

   /*
   *
   * 下载文件
   * */
   public void downLoadFile(final String url,final ProgressDialog pDialog,Handler h){
      progressDialog=pDialog;
      handler=h;
      new Thread() {
         public void run() {        
            HttpClient client = new DefaultHttpClient();
            HttpGet get = new HttpGet(url);
            HttpResponse response;
            try {
               response = client.execute(get);
               HttpEntity entity = response.getEntity();
               int length = (int) entity.getContentLength();
                                        progressDialog.setMax(length);
               InputStream is = entity.getContent();
               FileOutputStream fileOutputStream = null;
               if (is != null) {
                  File file = new File(
                        Environment.getExternalStorageDirectory(),
                        "Test.apk");
                  fileOutputStream = new FileOutputStream(file);
                        byte[] buf = new byte[10];   
                  int ch = -1;
                  int process = 0;
                  while ((ch = is.read(buf)) != -1) {       
                     fileOutputStream.write(buf, 0, ch);
                     process += ch;
                     progressDialog.setProgress(process);
                  }
               }
               fileOutputStream.flush();
               if (fileOutputStream != null) {
                  fileOutputStream.close();
               }
               down();
            } catch (ClientProtocolException e) {
               e.printStackTrace();
            } catch (IOException e) {
               e.printStackTrace();
            }
         }

      }.start();
   }


   //下载完毕,退出下载,进入安装APP步骤
   void down() {
      handler.post(new Runnable() {
         public void run() {
            progressDialog.cancel();
            update();
         }
      });
   }


   //安装最新版本APP
   void update() {
      Intent intent = new Intent(Intent.ACTION_VIEW);
      intent.setDataAndType(Uri.fromFile(new File(Environment
            .getExternalStorageDirectory(), "Test.apk")),
            "application/vnd.android.package-archive");
      context.startActivity(intent);
   }

   
}
 
github项目地址:https://github.com/huanghuaisong/android-php-appUpdate/
相关文章
|
编译器 Linux PHP
【Azure App Service】为部署在App Service上的PHP应用开启JIT编译器
【Azure App Service】为部署在App Service上的PHP应用开启JIT编译器
144 1
|
3月前
|
存储 小程序 Java
热门小程序源码合集:微信抖音小程序源码支持PHP/Java/uni-app完整项目实践指南
小程序已成为企业获客与开发者创业的重要载体。本文详解PHP、Java、uni-app三大技术栈在电商、工具、服务类小程序中的源码应用,提供从开发到部署的全流程指南,并分享选型避坑与商业化落地策略,助力开发者高效构建稳定可扩展项目。
|
9月前
|
JSON 自然语言处理 前端开发
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
508 72
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
|
7月前
|
存储 消息中间件 前端开发
PHP后端与uni-app前端协同的校园圈子系统:校园社交场景的跨端开发实践
校园圈子系统校园论坛小程序采用uni-app前端框架,支持多端运行,结合PHP后端(如ThinkPHP/Laravel),实现用户认证、社交关系管理、动态发布与实时聊天功能。前端通过组件化开发和uni.request与后端交互,后端提供RESTful API处理业务逻辑并存储数据于MySQL。同时引入Redis缓存热点数据,RabbitMQ处理异步任务,优化系统性能。核心功能包括JWT身份验证、好友系统、WebSocket实时聊天及活动管理,确保高效稳定的用户体验。
456 4
PHP后端与uni-app前端协同的校园圈子系统:校园社交场景的跨端开发实践
|
8月前
|
存储 监控 API
【Azure App Service】分享使用Python Code获取App Service的服务器日志记录管理配置信息
本文介绍了如何通过Python代码获取App Service中“Web服务器日志记录”的配置状态。借助`azure-mgmt-web` SDK,可通过初始化`WebSiteManagementClient`对象、调用`get_configuration`方法来查看`http_logging_enabled`的值,从而判断日志记录是否启用及存储方式(关闭、存储或文件系统)。示例代码详细展示了实现步骤,并附有执行结果与官方文档参考链接,帮助开发者快速定位和解决问题。
264 22
|
12月前
|
移动开发 小程序 前端开发
使用php开发圈子系统特点,如何获取圈子系统源码,社交圈子运营以及圈子系统的功能特点,圈子系统,允许二开,免费源码,APP 小程序 H5
开发一个圈子系统(也称为社交网络或社群系统)可以是一个复杂但非常有趣的项目。以下是一些关键特点和步骤,帮助你理解如何开发、获取源码以及运营一个圈子系统。
513 4
|
NoSQL 应用服务中间件 PHP
布谷一对一直播源码服务器环境配置及app功能
一对一直播源码阿里云服务器环境配置及要求
|
NoSQL PHP Redis
布谷语音app源码服务器环境配置及技术开发语言
布谷语音app源码服务器环境配置及技术语言研发。。
|
监控 网络安全 调度
Quartz.Net整合NetCore3.1,部署到IIS服务器上后台定时Job不被调度的解决方案
解决Quartz.NET在.NET Core 3.1应用中部署到IIS服务器上不被调度的问题,通常需要综合考虑应用配置、IIS设置、日志分析等多个方面。采用上述策略,结合细致的测试和监控,可以有效地提高定时任务的稳定性和可靠性。在实施任何更改后,务必进行充分的测试,以验证问题是否得到解决,并监控生产环境的表现,确保长期稳定性。
868 1
|
Go PHP 数据安全/隐私保护
【应用服务 App Service】Azure App Service 中如何安装mcrypt - PHP
【应用服务 App Service】Azure App Service 中如何安装mcrypt - PHP
125 2