安卓APP实现在线更新功能,适配Androidx和9.0,以及Tomcat服务器的配置

安卓APP实现在线更新功能,适配Androidx和9.0,以及Tomcat服务器的配置

1.需要用到的技术:

(1)Android
(2)Tomcat
(3)HTTP

2.效果展示:

在这里插入图片描述

3.实现步骤:

(1)Android项目的编写:

UpdateActivity.java活动

里面的端口号需要在Tomcat里的server.xml加以配置,后面有写。

import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.example.administrator.loginclient.R;
import com.example.administrator.loginclient.Updates.LoadingService;
import com.example.administrator.loginclient.Updates.Utils;
import com.example.administrator.loginclient.Updates.VersionInfoBean;
import java.io.File;

public class UpdateActivity extends BaseActivity {

    private Button btnRefresh;
    private VersionInfoBean versionInfoBean;
    private boolean isLoading;
    private MyReceive myReceive;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_update);
        btnRefresh = (Button)findViewById(R.id.btn_refresh);
        myReceive = new MyReceive();
        IntentFilter filter = new IntentFilter();
        filter.addAction("android.intent.action.loading_over");
        filter.addAction("android.intent.action.loading");

        btnRefresh.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
               if(isLoading){
                   Toast.makeText(UpdateActivity.this,"正在下载...",Toast.LENGTH_SHORT).show();

                }else{
                    getVersionInfoFromServer();
                }
            }
        });

    }
@Override
protected void onPause(){
super.onPause();
    myReceive = new MyReceive();
    IntentFilter filter = new IntentFilter();
    registerReceiver(myReceive,filter);
}
    /**
     * 从服务器获取版本最新的版本信息
     * 
     */
    @SuppressLint("ApplySharedPref")
    private void getVersionInfoFromServer(){

        versionInfoBean = new VersionInfoBean("1.1.1","http://localhost:8083/PersonalHomePage/app-debug.apk","1.更新了xxxxx功能"
                ,getExternalCacheDir()+"/apk");
        Log.v("下载成功...", getExternalCacheDir()+"/apk");
        SharedPreferences sharedPreferences = getSharedPreferences("data",MODE_PRIVATE);
        sharedPreferences.edit().putString("url",versionInfoBean.getDownloadUrl()).commit();
        sharedPreferences.edit().putString("path",versionInfoBean.getPath()).commit();//getExternalCacheDir获取到的路径 为系统为app分配的内存 卸载app后 该目录下的资源也会删除
        //比较版本信息

        try {
            int result = Utils.compareVersion(Utils.getVersionName(this),versionInfoBean.getVersionName());
            if(result==-1){//不是最新版本
                showDialog();

            }else{
                Toast.makeText(UpdateActivity.this,"已经是最新版本",Toast.LENGTH_SHORT).show();
            }
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }

    }
    private  void showDialog(){
        final Dialog dialog = new Dialog(UpdateActivity.this);
        LayoutInflater inflater = (LayoutInflater) UpdateActivity.this
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        TextView version,content;
        Button    left,right;
        View view = inflater.inflate(R.layout.version_update,null,false);
        version = (TextView)view.findViewById(R.id.version);
        content = (TextView)view.findViewById(R.id.content);
        left = (Button)view.findViewById(R.id.left);
        right = (Button)view.findViewById(R.id.right);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            content.setText(Html.fromHtml(versionInfoBean.getDesc(),Html.FROM_HTML_MODE_LEGACY));
        }else{
            content.setText(Html.fromHtml(versionInfoBean.getDesc()));
        }
        content.setMovementMethod(LinkMovementMethod.getInstance());
        version.setText("版本号:"+versionInfoBean.getVersionName());
        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        left.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                dialog.dismiss();
            }
        });
        right.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                dialog.dismiss();
                downloadNewVersionFromServer();

            }
        });
        dialog.setContentView(view);
        dialog.setCancelable(false);
        Window dialogWindow = dialog.getWindow();
        dialogWindow.setGravity(Gravity.CENTER);
        //dialogWindow.setWindowAnimations(R.style.ActionSheetDialogAnimation);
        WindowManager.LayoutParams lp = dialogWindow.getAttributes();
        WindowManager wm = (WindowManager)
                getSystemService(Context.WINDOW_SERVICE);
        lp.width =wm.getDefaultDisplay().getWidth()/10*9;
        dialogWindow.setAttributes(lp);
        dialog.show();
    }

    /**
     * 启动服务后台下载
     */
    private void downloadNewVersionFromServer(){
        if(new File(versionInfoBean.getPath()).exists()){
            new File(versionInfoBean.getPath()).delete();
        }
        Toast.makeText(UpdateActivity.this,"开始下载...",Toast.LENGTH_SHORT).show();

        LoadingService.startUploadImg(this);
    }

    /**
     * 定义广播接收者 接受下载状态
     */
    public class MyReceive extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if("android.intent.action.loading_over".equals(action)){
                isLoading = false;
            }else if("android.intent.action.loading".equals(action)){
                isLoading = true;
            }
        }
    }

}

工具类三个:

LoadingService.java

import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Build;
import android.widget.RemoteViews;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import androidx.core.content.FileProvider;
import com.example.administrator.loginclient.R;
import com.lidroid.xutils.HttpUtils;
import com.lidroid.xutils.exception.HttpException;
import com.lidroid.xutils.http.ResponseInfo;
import com.lidroid.xutils.http.callback.RequestCallBack;
import java.io.File;

public class LoadingService extends IntentService {
    private HttpUtils httpUtils;

    NotificationManager nm;
    private String url,path;
    private SharedPreferences sharedPreferences;

    public LoadingService(String name) {
        super(name);
    }
    public LoadingService() {
        super("MyService");

    }
    public static void startUploadImg(Context context)
    {
        Intent intent = new Intent(context, LoadingService.class);
        context.startService(intent);
    }

    public void onCreate() {
        super.onCreate();
        httpUtils = new HttpUtils();
        httpUtils.configCurrentHttpCacheExpiry(0);
        nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        sharedPreferences = getSharedPreferences("data",MODE_PRIVATE);
    }
    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        updateApk();
    }
    private void updateApk(){
        url = sharedPreferences.getString("url","");
        path = sharedPreferences.getString("path","");
        httpUtils.download(url,
                path , new RequestCallBack<File>() {
                    @Override
                    public void onLoading(final long total, final long current,
                                          boolean isUploading) {
                       createNotification(total,current);

                        sendBroadcast(new Intent().setAction("android.intent.action.loading"));//发送正在下载的广播

                        super.onLoading(total, current, isUploading);
                    }

                    @Override
                    public void onSuccess(ResponseInfo<File> arg0) {
                        nm.cancel(R.layout.notification_item);
                        Toast.makeText(LoadingService.this,"下载成功...",Toast.LENGTH_SHORT).show();

                            installApk();//下载成功 打开安装界面
                            stopSelf();//结束服务
                            sendBroadcast(new Intent().setAction("android.intent.action.loading_over"));//发送下载结束的广播
                    }
                    @Override
                    public void onFailure(HttpException arg0, String arg1) {
                        Toast.makeText(LoadingService.this,"下载失败...",Toast.LENGTH_SHORT).show();
                        sendBroadcast(new Intent().setAction("android.intent.action.loading_over"));//发送下载结束的广播
                        nm.cancel(R.layout.notification_item);
                        stopSelf();
                    }
                });
    }
    /**
     * 安装下载的新版本
     */
    protected void installApk() {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        File file = new File(path);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            // 7.0 以上
            try {
                Uri apkUri = FileProvider.getUriForFile(this, "com.jw.fileprovider", file);
                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
            }catch (Exception e){
            }
        } else {
            // 7.0以下
            Uri uri = Uri.fromFile(file);
            intent.setDataAndType(uri, "application/vnd.android.package-archive");
        }
        this.startActivity(intent);
    }

    private void createNotification(final long total, final long current){
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this,"default");
        builder.setSmallIcon(R.mipmap.ic_launcher)//必须要设置这个属性,否则不显示
               ;
        RemoteViews contentView = new RemoteViews(this.getPackageName(),R.layout.notification_item);
        contentView.setProgressBar(R.id.progress, (int)total, (int)current, false);
        builder.setOngoing(true);//设置左右滑动不能删除
      // Notification notification  = builder.build();
     // notification.contentView = contentView;
        String id = "channel_001";
        String name = "name";
        Notification notification = null;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

           NotificationChannel mChannel = new NotificationChannel(id, name, NotificationManager.IMPORTANCE_LOW);
            nm.createNotificationChannel(mChannel);
            notification = new Notification.Builder(LoadingService.this,"default")
                    .setChannelId(id)
                    .setContentTitle("客户端更新")
                    .setContentText("更新完毕")
                    .setSmallIcon(R.mipmap.ic_launcher).build();
           notification.contentView = contentView;



        } else {
            NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(LoadingService.this,"default")
                    .setContentTitle("更新了")
                    .setContentText("更新了xxxxxxxxx内容")
                    .setSmallIcon(R.mipmap.ic_launcher)
                    .setOngoing(true)
                    ;//无效
            notification = notificationBuilder.build();
            notification.contentView = contentView;
        }
        nm.notify(R.layout.notification_item,notification);//发送通知
    }
}

Utils.java

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
public class Utils {
    /**
     * 获取版本号
     *
     * @throws PackageManager.NameNotFoundException
     */
    public static String getVersionName(Context context) throws PackageManager.NameNotFoundException {
        // 获取packagemanager的实例
        PackageManager packageManager = context.getPackageManager();
        // getPackageName()是你当前类的包名,0代表是获取版本信息
        PackageInfo packInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
        String version = packInfo.versionName;
        return version;
    }

    /**
     * 版本号比较
     *0代表相等,1代表version1大于version2,-1代表version1小于version2
     * @param version1
     * @param version2
     * @return
     */
    public static int compareVersion(String version1, String version2) {
        if (version1.equals(version2)) {
            return 0;
        }
        String[] version1Array = version1.split("\\.");
        String[] version2Array = version2.split("\\.");
        int index = 0;
        // 获取最小长度值
        int minLen = Math.min(version1Array.length, version2Array.length);
        int diff = 0;
        // 循环判断每位的大小
        while (index < minLen
                && (diff = Integer.parseInt(version1Array[index])
                - Integer.parseInt(version2Array[index])) == 0) {
            index++;
        }
        if (diff == 0) {
            // 如果位数不一致,比较多余位数
            for (int i = index; i < version1Array.length; i++) {
                if (Integer.parseInt(version1Array[i]) > 0) {
                    return 1;
                }
            }

            for (int i = index; i < version2Array.length; i++) {
                if (Integer.parseInt(version2Array[i]) > 0) {
                    return -1;
                }
            }
            return 0;
        } else {
            return diff > 0 ? 1 : -1;
        }
    }
}

VersionInfoBean.java

public class VersionInfoBean {
    private String versionName;
    private String downloadUrl;
    private String desc;
    private String path;

    public VersionInfoBean(String versionName, String downloadUrl, String desc, String path) {
        this.versionName = versionName;
        this.downloadUrl = downloadUrl;
        this.desc = desc;
        this.path = path;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public String getVersionName() {
        return versionName;
    }

    public void setVersionName(String versionName) {
        this.versionName = versionName;
    }

    public String getDownloadUrl() {
        return downloadUrl;
    }

    public void setDownloadUrl(String downloadUrl) {
        this.downloadUrl = downloadUrl;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public VersionInfoBean(String versionName, String downloadUrl, String desc) {
        this.versionName = versionName;
        this.downloadUrl = downloadUrl;
        this.desc = desc;
    }
}

layout下的xml

activity_update.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".Activities.UpdateActivity">
    <!--
版本更新页面
-->
    <Button
        android:id="@+id/btn_refresh"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="版本更新"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

version_update.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"

    android:orientation="vertical">
    <!--
    安装页面
    -->
    <TextView
        android:id="@+id/title"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center_vertical|center_horizontal"
        android:paddingLeft="10dp"
        android:text="更新提示"
        android:textColor="#000000"
        android:textSize="18sp" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="#000000" />

    <TextView
        android:layout_marginTop="10dp"
        android:id="@+id/version"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="12dp"
        android:paddingRight="10dp"
        android:text="content"
        android:textSize="16sp" />
    <TextView
        android:layout_marginTop="14dp"
        android:id="@+id/content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="12dp"
        android:paddingRight="10dp"
        android:text="content"
        android:textSize="14sp" />


    <TextView
        android:layout_marginTop="10dp"
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="#f0f0f0" />

    <LinearLayout
        android:layout_marginTop="4dp"
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:orientation="horizontal"
        android:paddingLeft="10dp"
        android:paddingRight="10dp">

        <Button
            android:textColor="#000000"
            android:id="@+id/left"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:background="@null"
            android:gravity="center_vertical|center_horizontal"
            android:text="取消"
            android:textSize="16sp"
            />

        <TextView
            android:textColor="#f0f0f0"
            android:id="@+id/line"
            android:layout_width="1dp"
            android:layout_height="match_parent"
            android:layout_centerHorizontal="true"
            />

        <Button
            android:textColor="#d90c0c"
            android:id="@+id/right"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_alignParentRight="true"
            android:layout_weight="1"
            android:background="@null"
            android:gravity="center_vertical|center_horizontal"
            android:text="更新"
            android:textSize="16sp"
            />
    </LinearLayout>
</LinearLayout>

notification_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" android:layout_width="match_parent"
    android:paddingTop="8dp"
    android:layout_height="wrap_content">
    <!--
  下载通知页面-->
    <ImageView
        android:src="@mipmap/ic_launcher"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <LinearLayout
        android:layout_marginRight="6dp"
        android:layout_marginLeft="8dp"
        android:layout_gravity="center_vertical"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:textSize="14sp"
            android:text="正在下载"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        <ProgressBar
            android:layout_marginTop="4dp"
            style="?android:attr/progressBarStyleHorizontal"
            android:id="@+id/progress"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </LinearLayout>
</LinearLayout>

在res新建一个xml文件夹

在xml文件夹里新建资源文件update_apk_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <paths>
        <external-path name="update" path="." />
    </paths>
</resources>

在xml文件夹里新建资源文件network_security_config.xml

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true" />
</network-security-config>

添加的依赖:xUtils-2.6.14.jar 提取码:p5q3

   implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'androidx.core:core:1.3.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation files('libs/xUtils-2.6.14.jar')
      androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
  

manifest清单

1.在<manifest > ...</manifest> 里面加上权限:

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

2.在 <application> ... </application>里面加上:

 android:networkSecurityConfig="@xml/network_security_config"
   <!--Android 9.0加的-->
        <uses-library
            android:name="org.apache.http.legacy"
            android:required="false" 
   />
   
 							  <!--Activities是我的包名-->
    <receiver android:name=".Activities.UpdateActivity$MyReceive">
            <intent-filter>
                <action android:name="android.intent.action.DOWNLOAD_COMPETE"/>
            </intent-filter>
    </receiver>
   
    <activity android:name=".Activities.UpdateActivity"
            tools:ignore="InnerclassSeparator">
    </activity>

 <service
   <!-- .Updates.LoadingService里的 Updates是我的包名-->
            android:name=".Updates.LoadingService"
            android:process="system"
            tools:ignore="InnerclassSeparator" />
             <!-- 注册我系统级别的服务 这样退出app可以继续下载 -->
        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="com.jw.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">

            <!-- 元数据 -->
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/update_apk_paths" />
        </provider>
      

(2)Tomcat的配置:

<?xml version='1.0' encoding='utf-8'?>
<!--
  Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You under the Apache License, Version 2.0
  (the "License"); you may not use this file except in compliance with
  the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-->
<!-- Note:  A "Server" is not itself a "Container", so you may not
     define subcomponents such as "Valves" at this level.
     Documentation at /docs/config/server.html
 -->
<Server port="8005" shutdown="SHUTDOWN">
  <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
  <!-- Security listener. Documentation at /docs/config/listeners.html
  <Listener className="org.apache.catalina.security.SecurityListener" />
  -->
  <!--APR library loader. Documentation at /docs/apr.html -->
  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
  <!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html -->
  <Listener className="org.apache.catalina.core.JasperListener" />
  <!-- Prevent memory leaks due to use of particular java/javax APIs-->
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

  <!-- Global JNDI resources
       Documentation at /docs/jndi-resources-howto.html
  -->
  <GlobalNamingResources>
    <!-- Editable user database that can also be used by
         UserDatabaseRealm to authenticate users
    -->
    <Resource name="UserDatabase" auth="Container"
              type="org.apache.catalina.UserDatabase"
              description="User database that can be updated and saved"
              factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
              pathname="conf/tomcat-users.xml" />
  </GlobalNamingResources>

  <!-- A "Service" is a collection of one or more "Connectors" that share
       a single "Container" Note:  A "Service" is not itself a "Container",
       so you may not define subcomponents such as "Valves" at this level.
       Documentation at /docs/config/service.html
   -->
  <Service name="Catalina">

    <!--The connectors can use a shared executor, you can define one or more named thread pools-->
    <!--
    <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
        maxThreads="150" minSpareThreads="4"/>
    -->


    <!-- A "Connector" represents an endpoint by which requests are received
         and responses are returned. Documentation at :
         Java HTTP Connector: /docs/config/http.html (blocking & non-blocking)
         Java AJP  Connector: /docs/config/ajp.html
         APR (HTTP/AJP) Connector: /docs/apr.html
         Define a non-SSL HTTP/1.1 Connector on port 8080
    -->
    <Connector port="8083" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="443" />
    <!-- A "Connector" using the shared thread pool-->
    
  <!--  <Connector executor="tomcatThreadPool"
               port="8083" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="443" />
  -->  
    <!-- Define an SSL HTTP/1.1 Connector on port 8443
         This connector uses the BIO implementation that requires the JSSE
         style configuration. When using the APR/native implementation, the
         OpenSSL style configuration is required as described in the APR/native
         documentation -->

    <Connector port="443" protocol="org.apache.coyote.http11.Http11Protocol"
               maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
               clientAuth="false" sslProtocol="TLS" />


    <!-- Define an AJP 1.3 Connector on port 8009 -->
    <!--
    <Connector protocol="AJP/1.3"
               address="::1"
               port="8009"
               redirectPort="8443" />
    -->

    <!-- An Engine represents the entry point (within Catalina) that processes
         every request.  The Engine implementation for Tomcat stand alone
         analyzes the HTTP headers included with the request, and passes them
         on to the appropriate Host (virtual host).
         Documentation at /docs/config/engine.html -->

    <!-- You should set jvmRoute to support load-balancing via AJP ie :
    <Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
    -->
    <Engine name="Catalina" defaultHost="localhost">

      <!--For clustering, please take a look at documentation at:
          /docs/cluster-howto.html  (simple how to)
          /docs/config/cluster.html (reference documentation) -->
      <!--
      <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
      -->

      <!-- Use the LockOutRealm to prevent attempts to guess user passwords
           via a brute-force attack -->
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <!-- This Realm uses the UserDatabase configured in the global JNDI
             resources under the key "UserDatabase".  Any edits
             that are performed against this UserDatabase are immediately
             available for use by the Realm.  -->
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
               resourceName="UserDatabase"/>
      </Realm>

      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">

        <!-- SingleSignOn valve, share authentication between web applications
             Documentation at: /docs/config/valve.html -->
        <!--
        <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
        -->

        <!-- Access log processes all example.
             Documentation at: /docs/config/valve.html
             Note: The pattern used is equivalent to using pattern="common" -->
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log." suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b" />

<Context path="/PersonalHomePage" docBase="D:\PersonalHomePage" debug="0" reloadable="true" />

      </Host>
    </Engine>
  </Service>
</Server>

关于server.xml具体增加了什么操作?
其实是在 <Host>... </Host>里面加了以下的映射:

<Context path="/PersonalHomePage" docBase="D:\PersonalHomePage" debug="0" reloadable="true" />

在电脑的D盘里,新建一个名为PersonalHomePage的文件夹。
然后再Android studio里build apk,把.apk文件复制粘贴到PersonalHomePage里。文件名字不用更改了,因为我在代码里已经把该名字写好了。
在这里插入图片描述
最后打开Tomcat,再运行APP就行了。
注意:以上的实现是基于androidx。

相关推荐
©️2020 CSDN 皮肤主题: Age of Ai 设计师:meimeiellie 返回首页
实付 29.90元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值