在你的android设备运行java web应用程序

Android嵌入式设备产品在现场部署环境越来越复杂,很多情况下设备不便于卸载以及设备无触控屏,无法直接在设备上进行参数设置,故对设备进行远程设置就显得尤为方便,相似的应用可以类比我们路由器设置时使用的web页。

  本文利用jetty实现android设备运行java web应用程序。

  Android集成jetty,主要依赖以下jetty相关的jar(jetty、jetty-util),以AS环境下可以在项目的build.gradle中添加以下依赖:

dependencies {    ...    implementation 'org.mortbay.jetty:jetty:6.1.26'    implementation 'org.mortbay.jetty:jetty-util:6.1.26'    ...}

 AS工程目录如下:

主要文件说明如下:

WebHttpServer.java:http服务启动类,主要完成jetty环境准备,jetty服务启动。

JettyBootReceiver.java:开机启动监听,主要监听设备启动,设备启动后,启动相关的http服务。

DefaultHandler.java:web请求默认处理器,实现ip:port的访问方式。

wms.war:web应用程序的war包。

 

WebHttpServer.java源码:

package com.yx.demoservice;
import android.content.Context;import android.content.pm.PackageInfo;import android.os.Environment;import android.util.Log;
import com.yx.demoservice.constants.Constants;
import org.mortbay.jetty.Connector;import org.mortbay.jetty.Handler;import org.mortbay.jetty.Server;import org.mortbay.jetty.handler.HandlerCollection;import org.mortbay.jetty.nio.SelectChannelConnector;import org.mortbay.jetty.webapp.WebAppContext;import org.mortbay.util.IO;
import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.OutputStream;import java.util.jar.JarEntry;import java.util.jar.JarInputStream;
/** * WebHttpServer * * @author yx * @date 2019/6/19 13:04 */public class WebHttpServer {    private static final String TAG = "WebHttpServer";
    private Context mContext;    private static final String JETTY = "jetty";
    private static final File __JETTY_DIR;
    private static final String __WEBAPP_DIR = "webapps";    private static final String __ETC_DIR = "etc";    private static final String __CONTEXTS_DIR = "contexts";
    private static final String __TMP_DIR = "tmp";    private static final String __WORK_DIR = "work";
    private Thread progressThread;
    static {        __JETTY_DIR = new File(Environment.getExternalStorageDirectory(), JETTY);
        if (!__JETTY_DIR.exists()) {            __JETTY_DIR.mkdirs();        }    }
    public WebHttpServer(Context mContext) {        this.mContext = mContext;    }
    public void start() {        Log.w("IJettyBootReceiver", "-----WebHttpServer start ... ------------");        initIJetty();        startIJetty();        Log.w("IJettyBootReceiver", "-----WebHttpServer started ------------");    }
    private void startIJetty() {        if (isUpdateNeeded()) {            setupJetty();        }        startJettyService();    }
    private void startJettyService() {        try {            Server server = new Server();            Connector connector = new SelectChannelConnector();            connector.setPort(Constants.WEB_SERVICE_PORT);            server.setConnectors(new Connector[]{connector});
            String webapp = __JETTY_DIR + File.separator + __WEBAPP_DIR + File.separator +                    Constants.WEB_SERVICE_PACKAGE_NAME + File.separator;            WebAppContext root = new WebAppContext(webapp, Constants.WEB_SERVICE_NAME);            root.setResourceBase(webapp);            root.setClassLoader(Thread.currentThread().getContextClassLoader());            HandlerCollection handlers = new HandlerCollection();            handlers.setHandlers(new Handler[]{root, new DefaultHandler(mContext)});            server.addHandler(handlers);
            Log.w(TAG, "-----startJettyService webapp dir = " + webapp);            server.start();        } catch (Exception e) {            e.printStackTrace();        }    }
    private void setupJetty() {        progressThread = new ProgressThread();        progressThread.start();    }
    private boolean isUpdateNeeded() {        // if no previous version file, assume update is required        int storedVersion = getStoredJettyVersion();        if (storedVersion <= 0) {            Log.w(TAG, "storedVersion <=0 ");            return true;        }        try {            // if different previous version, update is required            PackageInfo pi =                    mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0);            if (pi == null) {                Log.w(TAG, "pi is null ");                return true;            }            if (pi.versionCode != storedVersion) {                Log.w(TAG, "pi is not match ");                return true;            }
            // if /sdcard/jetty/.update file exists, then update is required            File alwaysUpdate = new File(__JETTY_DIR, ".update");            if (alwaysUpdate.exists()) {                Log.i(TAG, "Always Update tag found " + alwaysUpdate);                return true;            }        } catch (Exception e) {            // if any of these tests go wrong, best to assume update is true?            Log.w(TAG, "these tests go wrong ");            return true;        }
        return false;    }
    private int getStoredJettyVersion() {        File jettyDir = __JETTY_DIR;        if (!jettyDir.exists()) {            return -1;        }        File versionFile = new File(jettyDir, "version.code");        if (!versionFile.exists()) {            return -1;        }        int val = -1;        ObjectInputStream ois = null;        try {            ois = new ObjectInputStream(new FileInputStream(versionFile));            val = ois.readInt();            return val;        } catch (Exception e) {            Log.e(TAG, "Problem reading version.code", e);            return -1;        } finally {            if (ois != null) {                try {                    ois.close();                } catch (Exception e) {                    Log.d(TAG, "Error closing version.code input stream", e);                }            }        }    }
    private void initIJetty() {        try {            extract(mContext.getResources().openRawResource(R.raw.wms));        } catch (Exception e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }
    /**     * Extract the war.     *     * @param warStream     * @throws IOException     */    private void extract(InputStream warStream) throws IOException {
        if (warStream == null) {            throw new IllegalArgumentException("No war file found");        }
        File jettyDir = getJettyInstallDir();        if (jettyDir == null) {            throw new IllegalStateException(mContext.getString(R.string.jetty_not_installed));        }
        File webappsDir = new File(jettyDir, __WEBAPP_DIR);        if (!webappsDir.exists()) {            webappsDir.mkdirs();            // throw new IllegalStateException (getString(R.string.jettyNotInstalled));        }
        File webapp = new File(webappsDir, Constants.WEB_SERVICE_PACKAGE_NAME);        if (!webapp.exists()) {            webapp.mkdirs();        }        JarInputStream jin = new JarInputStream(warStream);        JarEntry entry;        while ((entry = jin.getNextJarEntry()) != null) {            String entryName = entry.getName();            File file = new File(webapp, entryName);            if (entry.isDirectory()) {                // Make directory                if (!file.exists()) {                    file.mkdirs();                }            } else {                // make directory (some jars don't list dirs)                File dir = new File(file.getParent());                if (!dir.exists()) {                    dir.mkdirs();                }
                // Make file                FileOutputStream fout = null;                try {                    fout = new FileOutputStream(file);                    IO.copy(jin, fout);                } finally {                    IO.close(fout);                }
                // touch the file.                if (entry.getTime() >= 0) {                    file.setLastModified(entry.getTime());                }            }        }        IO.close(jin);    }
    /**     * Check to see if jetty has been installed.     *     * @return File of jetty install dir or null if not installed     */    private File getJettyInstallDir() {        File jettyDir = new File(Environment.getExternalStorageDirectory(), JETTY);        if (!jettyDir.exists()) {            return null;        }        return jettyDir;    }
    class ProgressThread extends Thread {
        private ProgressThread() {        }
        @Override        public void run() {            boolean updateNeeded = isUpdateNeeded();
            // create the jetty dir structure            File jettyDir = __JETTY_DIR;            if (!jettyDir.exists()) {                boolean made = jettyDir.mkdirs();                Log.i(TAG, "Made " + __JETTY_DIR + ": " + made);            }
            // Do not make a work directory to preserve unpacked            // webapps - this seems to clash with Android when            // out-of-date webapps are deleted and then re-unpacked            // on a jetty restart: Android remembers where the dex            // file of the old webapp was installed, but it's now            // been replaced by a new file of the same name. Strangely,            // this does not seem to affect webapps unpacked to tmp?            // Original versions of i-jetty created a work directory. So            // we will delete it here if found to ensure webapps can be            // updated successfully.            File workDir = new File(jettyDir, __WORK_DIR);            if (workDir.exists()) {                delete(workDir);                Log.i(TAG, "removed work dir");            }
            // make jetty/tmp            File tmpDir = new File(jettyDir, __TMP_DIR);            if (!tmpDir.exists()) {                boolean made = tmpDir.mkdirs();                Log.i(TAG, "Made " + tmpDir + ": " + made);            } else {                Log.i(TAG, tmpDir + " exists");            }
            // make jetty/webapps            File webappsDir = new File(jettyDir, __WEBAPP_DIR);            if (!webappsDir.exists()) {                boolean made = webappsDir.mkdirs();                Log.i(TAG, "Made " + webappsDir + ": " + made);            } else {                Log.i(TAG, webappsDir + " exists");            }
            // make jetty/etc            File etcDir = new File(jettyDir, __ETC_DIR);            if (!etcDir.exists()) {                boolean made = etcDir.mkdirs();                Log.i(TAG, "Made " + etcDir + ": " + made);            } else {                Log.i(TAG, etcDir + " exists");            }
            File webdefaults = new File(etcDir, "webdefault.xml");            if (!webdefaults.exists() || updateNeeded) {                // get the webdefaults.xml file out of resources                try {                    InputStream is = mContext.getResources().openRawResource(R.raw.webdefault);                    OutputStream os = new FileOutputStream(webdefaults);                    IO.copy(is, os);                    Log.i(TAG, "Loaded webdefault.xml");                } catch (Exception e) {                    Log.e(TAG, "Error loading webdefault.xml", e);                }            }
            File realm = new File(etcDir, "realm.properties");            if (!realm.exists() || updateNeeded) {                try {                    // get the realm.properties file out resources                    InputStream is =                            mContext.getResources().openRawResource(R.raw.realm_properties);                    OutputStream os = new FileOutputStream(realm);                    IO.copy(is, os);                    Log.i(TAG, "Loaded realm.properties");                } catch (Exception e) {                    Log.e(TAG, "Error loading realm.properties", e);                }            }
            // make jetty/contexts            File contextsDir = new File(jettyDir, __CONTEXTS_DIR);            if (!contextsDir.exists()) {                boolean made = contextsDir.mkdirs();                Log.i(TAG, "Made " + contextsDir + ": " + made);            } else {                Log.i(TAG, contextsDir + " exists");            }
            try {                PackageInfo pi =                        mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0);                if (pi != null) {                    setStoredJettyVersion(pi.versionCode);                }            } catch (Exception e) {                Log.w(TAG, "Unable to get PackageInfo for i-jetty");            }
            // if there was a .update file indicating an update was needed, remove it now            // we've updated            File update = new File(__JETTY_DIR, ".update");            if (update.exists()) {                update.delete();            }        }
        private void delete(File webapp) {            if (webapp.isDirectory()) {                File[] files = webapp.listFiles();                for (File f : files) {                    delete(f);                }                webapp.delete();            } else {                webapp.delete();            }        }    }
    private void setStoredJettyVersion(int version) {        File jettyDir = __JETTY_DIR;        if (!jettyDir.exists()) {            return;        }        File versionFile = new File(jettyDir, "version.code");        ObjectOutputStream oos = null;        try {            FileOutputStream fos = new FileOutputStream(versionFile);            oos = new ObjectOutputStream(fos);            oos.writeInt(version);            oos.flush();        } catch (IOException e) {            Log.e(TAG, "Problem writing jetty version", e);        } finally {            if (oos != null) {                try {                    oos.close();                } catch (IOException e) {                    Log.d(TAG, "Error closing version.code output stream", e);                }            }        }    }}

JettyBootReceiver.java源码:​​​​​​​

package com.yx.demoservice;
import android.content.BroadcastReceiver;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.util.Log;

/** * 服务开机自启动 * * @author yx * @date 2019/6/21 10:35 */public class JettyBootReceiver extends BroadcastReceiver {    private static final String TAG = "JettyBootReceiver";
    @Override    public void onReceive(Context context, Intent intent) {        Log.d(TAG, "JettyBootReceiver onReceive");
        if (intent != null && Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {            Log.d(TAG, " onReceive BOOT_COMPLETED");            Intent service = new Intent("com.yx.demoservice.WebService");            service.setPackage("com.yx.demoservice");            service.setComponent(                    new ComponentName("com.yx.demoservice", "com.yx.demoservice.WebService"));            context.startService(service);        }    }
}

DefaultHandler.java源码:

package com.yx.demoservice;
import android.content.Context;import android.util.Log;
import com.yx.demoservice.constants.Constants;
import org.mortbay.jetty.HttpMethods;import org.mortbay.jetty.handler.ContextHandlerCollection;

import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import android.net.EthernetManager;
import java.io.IOException;
/** * DefaultHandler * 主要处理ip+端口访问方式 * * @author yx * @date 2019/5/16 20:35 */public class DefaultHandler extends ContextHandlerCollection {    private static final String TAG = "DefaultHandler";
    private EthernetManager mEthManager = null;
    public DefaultHandler(Context mContext) {        super();        mEthManager = (EthernetManager) mContext.getSystemService(Context.ETHERNET_SERVICE);    }
    @Override    public void handle(String target, HttpServletRequest request, HttpServletResponse response,            int dispatch) throws IOException, ServletException {        String method = request.getMethod();        String uri = request.getRequestURI();        Log.w(TAG, "method =" + method + " ,uri =" + uri);        if (method.equals(HttpMethods.GET) && Constants.WEB_SERVICE_EMPTY.equals(uri)) {            if (mEthManager != null) {                String ip = mEthManager.getIpAddress();                if (ip != null) {                    String targetUrl = "http://" + ip + ":" + Constants.WEB_SERVICE_PORT +                            Constants.WEB_SERVICE_NAME + "/" + Constants.WEB_INDEX;                    Log.w(TAG, "targetUrl =" + targetUrl);                    response.sendRedirect(targetUrl);                    return;                }            }        }
    }}

最终运行效果:

设备开机,连接局域网,系统的在PC浏览器的访问方式为:设备ip+“:”+端口(8080)。如设备ip为192.168.1.196,则在PC浏览器中登录设备web管理系统的访问网址为:192.168.1.196:8080。

​​​​​​​

 

注意:

1.此demo工程在android嵌入式设备验证ok,注意运行是app或者service需以system组运行,即,AndroidManifest.xml声明如下:

android:sharedUserId="android.uid.system"

2.使用jetty相关的jar,经验证html的web应用支持,暂时不支持jsp,需要后续调试确认。

3.web应用程序集成到wms.war中,如war依赖其他jar包需要依赖到项目工程。

 

demo源码工程:

https://github.com/TomcatXiong/WebServiceDemo

                                                    

 

你可能感兴趣的:(android)