Browse Source

完成基本的 httpd

liuyuqi-dellpc 3 years ago
parent
commit
fa7ea19c5d

+ 13 - 0
README.md

@@ -0,0 +1,13 @@
+# NanoHTTPDemo
+
+## Spck 启发
+
+ 最近在android 编辑器软件 Spck Editor 看到很多 vscode 特色,全局搜索,git管理,PC 和 mobile 跨平台适配。拥有这么多功能安装包才 7M ,那么具体怎么实现的呢?简单反编译发现其实采用 vscode 类似 nodejs 基础的编码,通过 NanoHTTPD 启动一个本地服务器:端口7600。这样就可以直接使用已开源的IDE的优势,而且可以在 android 手机端使用。
+
+## NanoHTTPD 简单使用
+
+那么本项目学习一下 NanoHTTPD 的用法。
+
+(1)下载 nanohttpd-2.2.0.jar 包:  https://github.com/NanoHttpd/nanohttpd/releases
+
+(2)

+ 3 - 0
app/build.gradle

@@ -32,6 +32,9 @@ dependencies {
     implementation 'androidx.appcompat:appcompat:1.2.0'
     implementation 'com.google.android.material:material:1.2.1'
     implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
+    implementation fileTree(dir: 'libs', include: ['*.aar', '*.jar'], exclude: [])
+
+
     testImplementation 'junit:junit:4.+'
     androidTestImplementation 'androidx.test.ext:junit:1.1.2'
     androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

BIN
app/libs/nanohttpd-2.2.0.jar


+ 18 - 1
app/src/main/AndroidManifest.xml

@@ -1,14 +1,30 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     package="me.yoqi.android.nanohttpdemo">
 
+    <uses-permission
+        android:name="android.permission.WRITE_EXTERNAL_STORAGE"
+        tools:ignore="ScopedStorage" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <!--    网络权限-->
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.VIBRATE" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.MICROPHONE" />
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
+    <uses-permission android:name="android.permission.CAMERA" />
+
     <application
         android:allowBackup="true"
         android:icon="@mipmap/ic_launcher"
         android:label="@string/app_name"
         android:roundIcon="@mipmap/ic_launcher_round"
         android:supportsRtl="true"
-        android:theme="@style/Theme.NanoHTTPDemo">
+        android:theme="@style/Theme.NanoHTTPDemo"
+        android:usesCleartextTraffic="true">
         <activity android:name=".MainActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -16,6 +32,7 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+        <service android:name=".service.LocalService" />
     </application>
 
 </manifest>

+ 73 - 1
app/src/main/java/me/yoqi/android/nanohttpdemo/MainActivity.java

@@ -1,14 +1,86 @@
 package me.yoqi.android.nanohttpdemo;
 
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.webkit.WebResourceRequest;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.Button;
+
 import androidx.appcompat.app.AppCompatActivity;
 
-import android.os.Bundle;
+import me.yoqi.android.nanohttpdemo.service.LocalService;
 
 public class MainActivity extends AppCompatActivity {
+    Button btnStart;
+    WebView webView;
+    Context mContext;
+    boolean flag = false; //按钮 httpd 状态
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
+        mContext = this;
+        initView();
+    }
+
+    void initView() {
+        btnStart = findViewById(R.id.btn_starthttpd);
+        webView = findViewById(R.id.wv_main);
+        WebSettings webSettings = webView.getSettings();
+        webSettings.setJavaScriptEnabled(true);
+
+//设置自适应屏幕,两者合用
+        webSettings.setUseWideViewPort(true); //将图片调整到适合webview的大小
+        webSettings.setLoadWithOverviewMode(true); // 缩放至屏幕的大小
+
+//缩放操作
+        webSettings.setSupportZoom(true); //支持缩放,默认为true。是下面那个的前提。
+        webSettings.setBuiltInZoomControls(true); //设置内置的缩放控件。若为false,则该WebView不可缩放
+        webSettings.setDisplayZoomControls(false); //隐藏原生的缩放控件
+
+//其他细节操作
+        webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //关闭webview中缓存
+        webSettings.setAllowFileAccess(true); //设置可以访问文件
+        webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //支持通过JS打开新窗口
+        webSettings.setLoadsImagesAutomatically(true); //支持自动加载图片
+        webSettings.setDefaultTextEncodingName("utf-8");//设置编码格式
+
+
+        btnStart.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (!flag) {
+                    startHttpd();
+                    flag = true;
+                    btnStart.setText("已启动");
+                    webView.loadUrl("http://localhost:7600");
+                    webView.setWebViewClient(new WebViewClient() {
+                        @Override
+                        public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
+                            return super.shouldOverrideUrlLoading(view, request);
+                        }
+                    });
+                } else {
+                    stopHttpd();
+                    flag = false;
+                    btnStart.setText("已关闭");
+                }
+            }
+        });
+    }
+
+    void startHttpd() {
+        Intent intent = new Intent(mContext, LocalService.class);
+        startService(intent);
+    }
+
+    void stopHttpd() {
+        Intent intent = new Intent(mContext, LocalService.class);
+        stopService(intent);
     }
 }

+ 31 - 0
app/src/main/java/me/yoqi/android/nanohttpdemo/net/LocalHttpService.java

@@ -0,0 +1,31 @@
+package me.yoqi.android.nanohttpdemo.net;
+
+import fi.iki.elonen.NanoHTTPD;
+
+/**
+ * @author liuyuqi.gov@msn.cn
+ * @date 12/18/2020
+ */
+public class LocalHttpService extends NanoHTTPD {
+
+    /**
+     * 覆写构造方法
+     *
+     * @param port
+     */
+    public LocalHttpService(int port) {
+        super(port);
+    }
+
+    @Override
+    public Response serve(IHTTPSession session) {
+        StringBuilder builder = new StringBuilder();
+        builder.append("<!DOCTYPE html><html><body>");
+        builder.append("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />");
+        builder.append("\t<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"/>\n" +
+                "  \t<meta content=\"width=device-width,initial-scale=1.0\" name=\"viewport\">");
+        builder.append("服务已经启动啦,可以正常使用啦!");
+        builder.append("</body></html>\n");
+        return newFixedLengthResponse(builder.toString());
+    }
+}

+ 42 - 0
app/src/main/java/me/yoqi/android/nanohttpdemo/service/LocalService.java

@@ -0,0 +1,42 @@
+package me.yoqi.android.nanohttpdemo.service;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+import androidx.annotation.Nullable;
+
+import java.io.IOException;
+
+import me.yoqi.android.nanohttpdemo.net.LocalHttpService;
+
+/**
+ * @author liuyuqi.gov@msn.cn
+ * @date 12/18/2020
+ */
+public class LocalService extends Service {
+
+    @Nullable
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    /**
+     *  启动服务的时候启动 httpd
+     * @param intent
+     * @param flags
+     * @param startId
+     * @return
+     */
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        LocalHttpService localHttpService = new LocalHttpService(7600);
+        try {
+            localHttpService.start();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return super.onStartCommand(intent, flags, startId);
+    }
+}

+ 22 - 0
app/src/main/res/layout/activity_main.xml

@@ -7,12 +7,34 @@
     tools:context=".MainActivity">
 
     <TextView
+        android:id="@+id/tv_xx"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="Hello World!"
         app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintHorizontal_bias="0.498"
         app:layout_constraintLeft_toLeftOf="parent"
         app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintVertical_bias="0.098" />
+
+    <Button
+        android:id="@+id/btn_starthttpd"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="128dp"
+        android:text="启动"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent" />
 
+    <WebView
+        android:id="@+id/wv_main"
+        android:layout_width="match_parent"
+        android:layout_height="500dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="0.0"
+        app:layout_constraintStart_toStartOf="parent"></WebView>
+
 </androidx.constraintlayout.widget.ConstraintLayout>