整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          華為鴻蒙-完整服務卡片項目開發「為Bilibili添

          華為鴻蒙-完整服務卡片項目開發「為Bilibili添加服務卡片」

          這是一款純鴻蒙版的嗶哩嗶哩服務卡片應用。

          6月2日鴻蒙發布,今年的六月已經被鴻蒙刷屏了。從安卓到鴻蒙,最直觀的變化應該就是服務卡片了。我也是在學習鴻蒙的同時,實際體驗一下服務卡片的開發。 給大家看看最終的效果。

          接下來分享下我的制作過程,我使用的開發環境是 IDE:DevEco Studio 2.1 Release SDK:API Version 5

          軟件安裝和項目建立的部分就跳過了,相信大家都比較熟悉了。直奔主題服務卡片的制作。

          一、服務卡片設計

          首先要先了解服務卡片,都有哪些尺寸,支持哪些組件,使用什么語言。然后規劃好要實現哪些功能。

          1.尺寸規格

          服務卡片有4種尺寸,分別是微卡片、小卡片、中卡片、大卡片。官方提供了4種基礎模板,12種高級模板。可以選擇。基礎模板如下圖

          2.功能設計

          服務卡片設計的初衷就是信息顯示、服務直達。依照這個原則,我找了幾個Bilibili中我比較常用的功能,來制作服務卡片,比如追番列表。

          3.開發語言

          看下表就一目了然了,就是推薦JS。文檔中心

          二、界面實現

          本著學習的目的,卡片界面就不使用模板了。不過我們還是要通過IDE>>File>>New>>Service Widget來添加服務卡片,這樣添加IDE會自動添加配置和管理相關文件。然后服務卡片的界面重新編寫。服務卡片常用的的容器組件有div、list、stack、swiper等。我使用了4種尺寸的卡片,并盡可能的使用到所有的容器組件。

          div:基礎容器組件

          就是用來劃分區域的。比較常用。比如追番服務卡片。效果如圖,代碼如下

          <div class="div_root"  ><!--在服務卡片設置一個 根div 橫向布局-->
          
              <div class="div_container"><!--在根div 橫向放置4個div,每個div內部從上往下排列-->
                      <image class="item_image" src="{{ src1 }}" onclick="routerEvent1"></image>
                      <text class="item_title">{{ itemTitle1 }}</text>
                      <text class="item_content">{{ itemContent1 }}</text>
              </div>
          
              <div class="div_container"><!--第二列-->
                  <image class="item_image" src="{{ src2 }}" onclick="routerEvent2"></image>
                  <text class="item_title">{{ itemTitle2 }}</text>
                  <text class="item_content">{{ itemContent2 }}</text>
              </div>
          
              <div class="div_container"><!--第三列-->
                  <image class="item_image" src="{{ src3 }}" onclick="routerEvent3"></image>
                  <text class="item_title">{{ itemTitle3 }}</text>
                  <text class="item_content">{{ itemContent3 }}</text>
              </div>
          
              <div class="div_container"><!--第四列-->
                  <image class="item_image" src="{{ src4 }}" onclick="routerEvent4"></image>
                  <text class="item_title">{{ itemTitle4 }}</text>
                  <text class="item_content">{{ itemContent4 }}</text>
              </div>
          
          </div>


          .div_root {
              flex-direction: row;        /*flex容器主軸方向,row:水平方向從左到右。*/
              justify-content: center;    /*flex容器當前行的主軸對齊格式,center:項目位于容器的中心。*/
              margin:6px;                 /*外邊距屬性:只有一個值時,這個值會被指定給全部的四個邊。*/
              border-radius: 10px;		/*設置元素的外邊框圓角半徑。*/
          }
          
          .div_container {
              flex-direction: column;         /*flex容器主軸方向,column:垂直方向從上到下。*/
              justify-content: flex-start;    /*flex容器當前行的主軸對齊格式,flex-start:項目位于容器的開頭。*/
              margin:6px;
          }
          
          .item_image {
              height: 60%;					/*卡片在不同設備,尺寸會發生變化,所以最好使用百分比進行標注。*/
              border-radius: 10px;
              background-color: #F1F3F5;      /*設置背景顏色。*/
          }
          @media (dark-mode: true) {          /*當前系統為深色模式時,使用這里的配置,如果沒有顏色設置,可以不設置*/
              .item_image {
                  height: 60%;
                  border-radius: 10px;
                  background-color: #202224;
              }
          }
          
          .item_title {
              margin-top: 10px;           /*設置上邊距。*/
              font-size: 12px;            /*設置文本的尺寸。*/
              font-weight: bold;          /*設置文本的字體粗細。取值[100, 900],默認為400。*/
              max-lines:1;                /*設置文本的最大行數。*/
              text-overflow: ellipsis;    /*根據父容器大小顯示,顯示不下的文本用省略號代替。需配合max-lines使用。*/
              color: #e5000000;           /*設置文本的顏色。*/
          }
          
          .item_content {
              margin-top: 5px;
              font-size: 9px;
              font-weight: bold;
              text-overflow: ellipsis;
              max-lines:1;
              color: #99000000;
          }
          
          

          Note:其實這個服務卡片的布局,每一列的內容都是相同的,是應該使用list組件的。

          list:列表容器組件

          就如上面所說的連續相同的部分,可以使用這個組件,List不但可以顯示更多的內容,而且代碼更少。效果圖如下

          <list class="list">
              <list-item for="{{cards}}" class="list-item">
                  <div class="div" onclick="sendRouteEvent">
                      <image class="item_image" src="{{ $item.pic }}"></image>
                      <text class="item_name">{{ $item.name }}</text>
                      <text class="item_title">{{ $item.title }}</text>
                  </div>
              </list-item>
          </list>
          
          


          .list{
              align-items:center; /*list每一列交叉軸上的對齊格式:元素在交叉軸居中*/
          }
          .list-item{
              border-radius: 15px;
              background-color: #f2f2f2;
              margin-bottom: 5px;
          }
          .div{
              flex-direction: column;
          }
          
          .item_image {
              border-top-right-radius: 15px;
              border-top-left-radius: 15px;
          }
          
          .item_name {
              margin:5px 8px 0px;
              font-size: 12px;
              color: #262626;
          }
          
          .item_title{
              margin:3px 8px 8px;
              font-size: 10px;
              color: #AAAAAA;
              max-lines: 2;
              text-overflow: ellipsis;    /* 省略號 */
          }

          stack:堆疊容器組件

          簡單來說就是可以在一張圖片上堆疊顯示另一張圖片,例如下圖藍框的圖片覆蓋在紅框圖片的上面。

          swiper:滑動容器組件

          正常情況下swiper是可以實現上下、左右滑動操作的。但是放置在桌面上的服務卡片,在左右滑動操作的時候,會使系統分不清楚用戶是要左右滑動屏幕,還是左右滑動卡片。所以目前服務卡片的swiper容器是不支持手勢滑動切換子組件的。下圖是通過點擊圖片側面的控制條實現上下滑動的。但是我個人覺得上下滑動其實還是挺好用的,畢竟在list組件上是可以上下滑動的,只可惜目前還不支持。

          總結:服務卡片的設計比較簡單,零基礎也沒關系,官方還貼心的準備了模板。只要挑選模板,設置變量也能快速構建。

          三、API數據請求

          卡片設計好之后,就需要通過Bilibili的API來獲取數據了。主要就是給權限添加依賴,然后發送網絡請求,通過API獲取JSON的返回值,然后解析JSON得到我們需要的數據。

          1.添加聯網權限

          要在config.json配置文件的module中添加:“reqPermissions”: [{“name”:“ohos.permission.INTERNET”}],

          {
            ... ...
            "module": {
            	... ...
            	"reqPermissions": [{"name":"ohos.permission.INTERNET"}]
            }
          }

          2.添加依賴包

          找到entry/build.gradle文件,在dependencies下添加

          dependencies {
              implementation fileTree(dir: 'libs', include: ['*.jar', '*.har'])
              testImplementation 'junit:junit:4.13'
              ohosTestImplementation 'com.huawei.ohos.testkit:runner:1.0.0.100'
          
              // ZZRHttp 可以單獨一個進程進行http請求
              implementation 'com.zzrv5.zzrhttp:ZZRHttp:1.0.1'
              
              // fastjson 可以解析JSON格式
              implementation group: 'com.alibaba', name: 'fastjson', version: '1.2.75'
          }

          3.http請求

          以獲取粉絲數為例。如果在瀏覽器中輸入 https://api.bilibili.com/x/relation/stat?vmid=383565952 (其中vmid:是要查詢的用戶ID)

          follower的值就是粉絲數。

          網絡訪問我們可以使用HttpURLConnection,或者okhttp等依賴包,但是需要開啟子線程、處理異常等操作,所以這里使用的是ZZR老師封裝好的ZZRHttp

          代碼實現:

          //獲取Bilibili粉絲數,這里就要用到第二步我們添加的ZZRHttp
          String url="https://api.bilibili.com/x/relation/stat?vmid=383565952";
          ZZRHttp.get(url, new ZZRCallBack.CallBackString() {
              @Override
              public void onFailure(int i, String s) {
                  HiLog.info(TAG, "API返回失敗");
              }
              @Override
              public void onResponse(String s) {
                  HiLog.info(TAG, "API返回成功");
                  // 如果返回成功,返回的結果就會保存在 String s 中。
                  // s={"code":0,"message":"0","ttl":1,"data":{"mid":383565952,"following":70,"whisper":0,"black":0,"follower":5384}} 
              }
          });

          4.解析JSON

          得到的是JSON格式的返回值,要得到follower的值,還需要對JSON進行數據解析。

          先按照JSON的內容,生成JAVA類。代碼如下。可以自己寫,也可以百度搜 ”JSON生成Java實體類“,可直接生成。

          public class BilibiliFollower {
              public static class Data{
                  private int follower;
                  public int getFollower() {
                      return follower;
                  }
                  public void setFollower(int follower) {
                      this.follower=follower;
                  }
              }
              private BilibiliFollower.Data data;
              public BilibiliFollower.Data getData() {
                  return data;
              }
              public void setData(BilibiliFollower.Data data) {
                  this.data=data;
              }
          }


          //解析JSON,使用第二步我們添加的fastjson包
          try {
              //1.調用fastjson解析,結果保存在JSON對應的類
              BilibiliFollower bilibiliFollower=JSON.parseObject(s,BilibiliFollower.class);
              //2.get方法獲取解析內容
              BilibiliFollower.Data data=bilibiliFollower.getData();
              System.out.println("解析成功"+data.getFollower());
          
          } catch (Exception e) {
              HiLog.info(TAG, "解析失敗");
          }

          總結:一定要添加聯網權限不然是獲取不到數據的。添加了2個依賴包,可以很方便的提取數據。獲取其他的卡片數據的方式同理,不過代碼比較多,就不一一展示了,感興趣可以下載全量代碼看。

          四、數據更新

          要想將數據更新到服務卡片,得先了解服務卡片的運作機制。如果是通過IDE>>File>>New>>Service Widget添加的服務卡片,那么在MainAbility中會添加卡片的生命周期回調方法,參考下面的代碼。

          public class MainAbility extends Ability {
              
              ... ...
          
              protected ProviderFormInfo onCreateForm(Intent intent) {...}//在服務卡片上右擊>>服務卡片(或上滑)時,通知接口
          
              protected void onUpdateForm(long formId) {...}//在服務卡片請求更新,定時更新時,通知接口
          
              protected void onDeleteForm(long formId) {..}//在服務卡片被刪除時,通知接口
          
              protected void onTriggerFormEvent(long formId, String message) {...}//JS服務卡片click時,通知接口
          }

          1.定時更新

          按照上述分析,我們只需要在config.json中開啟服務卡片的周期性更新,在onUpdateForm(long formId)方法下執行數據獲取更新。

          config.json文件“abilities”的forms模塊配置細節如下

          "forms": [
            {
                "jsComponentName": "widget2",
                "isDefault": true,
                "scheduledUpdateTime": "10:30",//定點刷新的時刻,采用24小時制,精確到分鐘。"updateDuration": 0時,才會生效。
                "defaultDimension": "1*2",
                "name": "widget2",
                "description": "This is a service widget",
                "colorMode": "auto",
                "type": "JS",
                "supportDimensions": [
                    "1*2"
                ],
                "updateEnabled": true,	//表示卡片是否支持周期性刷新
                "updateDuration": 1		//卡片定時刷新的更新周期,1為30分鐘,2為60分鐘,N為30*N分鐘
            }
          ]

          這樣結合我們在上一步獲取API數據,解析JSON,開啟服務卡片的周期性更新,就可以在updateFormData()實現服務卡片的數據更新了。截取follower數據更新的部分代碼如下

          public void updateFormData(long formId, Object... vars) {
              HiLog.info(TAG, "update form data: formId" + formId);
          
              //這部分用來獲取粉絲數
              String url="https://api.bilibili.com/x/relation/stat?vmid=383565952";
              ZZRHttp.get(url, new ZZRCallBack.CallBackString() {
                  @Override
                  public void onFailure(int i, String s) {HiLog.info(TAG, "API返回失敗");}
                  @Override
                  public void onResponse(String s) {
                      HiLog.info(TAG, "API返回成功");
                      try {
                          //1.調用fastjson解析,結果保存在JSON對應的類
                          BilibiliFollower bilibiliFollower=JSON.parseObject(s,BilibiliFollower.class);
                          //2.get方法獲取解析內容
                          BilibiliFollower.Data data=bilibiliFollower.getData();
                          System.out.println("解析成功"+data.getFollower());
          
                          //這部分用來更新卡片信息
                          ZSONObject zsonObject=new ZSONObject(); //1.將要刷新的數據存放在一個ZSONObject實例中
                          zsonObject.put("follower",data.getFollower()); //2.更新數據,data.getFollower()就是在API數據請求中獲取的粉絲數。
                          FormBindingData formBindingData=new FormBindingData(zsonObject); //3.將其封裝在一個FormBindingData的實例中
                          try {
                              ((MainAbility)context).updateForm(formId,formBindingData); //4.調用MainAbility的方法updateForm(),并將formBindingData作為第二個實參
                          } catch (FormException e) {
                              e.printStackTrace();
                              HiLog.info(TAG, "更新卡片失敗");
                          }
                      } catch (Exception e) {
                          HiLog.info(TAG, "解析失敗");
                      }
                  }
              });
          }

          2.手動更新

          正常來說這樣就可以正常更新數據了,但是會有個問題。就是在服務卡片首次創建添加到桌面的時候,在添加完的至少30分鐘里,數據是不會更新的。此時如果在index.json中設置初始信息,那么在添加完成的前30分鐘數據都是寫死在data中的。如果不設置初始信息那么卡片就是空白的。

          所以按照前面服務卡片的運作機制的分析,我們還需要在卡片初始化onCreateForm()的時候進行一次更新。這個非常簡單用onCreateForm()調用onUpdateForm(formId)即可。

          @Override
          protected ProviderFormInfo onCreateForm(Intent intent) {
              ... ...
          
          	//初始化時先在線更新一下卡片
              onUpdateForm(formId);
          
              return formController.bindFormData();
          }

          總結:這里的onUpdateForm(formId)中API的網絡請求一定要新開一個子線程,不然會影響頁面加載。這也是前面說的用ZZRhttp的原因。不過現在也遇到一個問題,當卡片數量變多時,同時在線更新這么多的卡片會變得非常緩慢,這個問題還有待解決。

          五、功能直達

          目前服務卡片僅支持click通用事件,事件類型:跳轉事件(router)和消息事件(message)。詳細說明參考官方文檔

          1.跳轉事件

          接下來實現與服務卡片的交互,當點擊服務卡片時,會跳轉到相應的頁面,所以這里使用跳轉事件。以番劇更新的卡片為例

          1.首先我們要先添加一個要跳轉的頁面。如下圖所示添加一個Page Ability,比如:VideoSlice

          2.新建完成之后會增加VideoSlice和 slice/VideoSliceSlice 兩個文件,和base/layout/ability_bilibili_page.xml頁面文件

          @Override
          public void onStart(Intent intent) {
           super.onStart(intent);
           super.setUIContent(ResourceTable.Layout_ability_video);
          
           Text text=(Text) findComponentById(ResourceTable.Id_text);
           text.setText("頁面跳轉中");
          
           // 隨機圖片數組
           int[] resource={ResourceTable.Media_36e,ResourceTable.Media_36g,ResourceTable.Media_36h,ResourceTable.Media_38p};
           Component component=findComponentById(ResourceTable.Id_image);
           if (component instanceof Image) {
               Image image=(Image) component;
               image.setPixelMap(resource[(int)(Math.random()*3)]);//隨機顯示一張圖片
           }
          
           String url="https://m.bilibili.com";
          
           String param=intent.getStringParam("params");//從intent中獲取 跳轉事件定義的params字段的值
           if(param !=null){
               ZSONObject data=ZSONObject.stringToZSON(param);
               url=data.getString("url");
           }
          
           webview(url);
          }
          //啟動webview
          public void webview(String url){
           WebView webView=(WebView) findComponentById(ResourceTable.Id_webview);
           webView.getWebConfig().setJavaScriptPermit(true);  // 如果網頁需要使用JavaScript,增加此行;如何使用JavaScript下文有詳細介紹
           webView.load(url);
          }

          3.增加webview,將頁面默認的Text控件修改為webview

          <?xml version="1.0" encoding="utf-8"?>
          <DirectionalLayout
              xmlns:ohos="http://schemas.huawei.com/res/ohos"
              ohos:height="match_parent"
              ohos:width="match_parent"
              ohos:alignment="center"
              ohos:orientation="vertical">
              <ohos.agp.components.webengine.WebView
                  ohos:id="$+id:webview"
                  ohos:height="match_parent"
                  ohos:width="match_parent">
              </ohos.agp.components.webengine.WebView>
          
          </DirectionalLayout>

          4.在index.hml中給要觸發的控件上添加onclick,比如:onclick=“routerEvent1”

          <div class="div_root"  ><!--在服務卡片設置一個 根div 橫向布局-->
              <div class="div_container"><!--在根div 橫向放置4個div,每個div內部從上往下排列-->
                      <image class="item_image" src="{{ src1 }}" onclick="routerEvent1"></image>
                      <text class="item_title">{{ itemTitle1 }}</text>
                      <text class="item_content">{{ itemContent1 }}</text>
              </div>
              ... ...
          </div>

          5.在index.json中,添加對應的actions,跳轉事件要多加一個參數"abilityName",指定要跳轉的頁面,并且攜帶參數url。

          {
            "data": {
            },
            "actions": {
                  "routerEvent1": {
                      "action": "router",
                      "bundleName": "com.liangzili.servicewidget",
                      "abilityName": "com.liangzili.servicewidget.VideoSlice",
                      "params": {
                          "url": "{{url1}}"
                      }
                  },
                "routerEvent2": {
                ... ...    
          }

          2.消息事件

          這里使用視頻動態服務卡片,做一個消息事件的測試,效果如下圖,點擊左右邊,實現服務卡片的滑動。在小卡片上這樣的操作體驗不好。所以消息事件中的例子,只是為了測試,并沒有加到項目里。

          1.在index.hml中給要觸發的控件上添加onclick,比如:onclick=“sendMessageEvent”

          <-- 為了方便測試,直接將onclick添加在左右兩側的div組件上 -->
          <div class="div" onclick="sendMessageEvent0">
              <image class="item_image" src="{{ src0 }}"></image>
              <text class="item_title">{{ itemTitle0 }}</text>
              <text class="item_content">{{ itemContent0 }}</text>
          </div>
          <div class="div" onclick="sendMessageEvent1">
              <image class="item_image" src="{{ src1 }}"></image>
              <text class="item_title">{{ itemTitle1 }}</text>
              <text class="item_content">{{ itemContent1 }}</text>
          </div>

          2.在index.json中,添加對應的actions

          {
            "data": {
            },
              "actions": {
                  "sendMessageEvent0": {
                      "action": "message",
                      "params": {
                          "p1": "left",
                          "index": "{{index}}"
                      }
                  },
                  "sendMessageEvent1": {
                      "action": "message",
                      "params": {
                          "p1": "right",
                          "index": "{{index}}"
                      }
                  }
              }
          }

          3.如果是消息事件(message)當點擊帶有onclick的控件時,會觸發MainAbility下的這個函數

          @Override
          protected void onTriggerFormEvent(long formId, String message) {
              HiLog.info(TAG, "onTriggerFormEvent: " + message); //params的內容就通過message傳遞過來
              super.onTriggerFormEvent(formId, message);
              FormControllerManager formControllerManager=FormControllerManager.getInstance(this);
              FormController formController=formControllerManager.getController(formId);//通過formId得到卡片控制器
              formController.onTriggerFormEvent(formId, message);//接著再調用,對應的控制器 WidgetImpl
          }

          4.最后調用卡片控制器 WidgetImpl 中的onTriggerFormEvent()

          public void onTriggerFormEvent(long formId, String message) {
           HiLog.info(TAG, "onTriggerFormEvent."+message);
           
           //先獲取message中的參數
           ZSONObject data=ZSONObject.stringToZSON(message);
           String p1=data.getString("p1");
           Integer index=data.getIntValue("index");
          
           ZSONObject zsonObject=new ZSONObject(); 	//將要刷新的數據存放在一個ZSONObject實例中
           Integer indexMax=2;                   	//有N個滑塊組件就設置N-1
           if(p1.equals("right")){                 	//判斷點擊方向,如果是右側
               if(index==indexMax){index=-1;}  	//實現循環滾動
               index=index+1;
               zsonObject.put("index",index);
           }else {                                 	//判斷點擊方向,如果是左側
               if(index==0){index=indexMax+1;} 	//實現循環滾動
               index=index-1;
               zsonObject.put("index",index);
           }
          
           FormBindingData formBindingData=new FormBindingData(zsonObject); 
           try {
               ((MainAbility)context).updateForm(formId,formBindingData);
           } catch (FormException e) {
               e.printStackTrace();
               HiLog.info(TAG, "更新卡片失敗");
           }
          }

          3.list跳轉事件

          list組件只能添加一個onclick,而且在點擊的同時還需要獲取點擊的是list列表中的哪一項,這個比較特殊。

          <list class="list" else>
              <list-item for="{{list}}" class="list-item">
                  <div class="div" onclick="sendRouteEvent">
          			... ...
                  </div>
              </list-item>
          </list>

          這個坑折磨了我好久,最終我發現在index.json中,可以使用item,item,idx獲取到hml頁面list的元素變量和索引。但是在官方文檔并沒有找到相關的內容,嘗試了很久才解決這個問題。之后的部分就和跳轉事件一樣了,使用Video頁面解析url進行播放就可以了。

            "actions": {
              "sendRouteEvent": {
                "action": "router",
                "bundleName": "com.liangzili.demos",
                "abilityName": "com.liangzili.demos.Video",
                "params": {
                  "url": "{{$item.short_url}}",
                  "index": "{{$idx}}"
                }
              }
            }

          總結:解決了list的點擊事件之后,才發現這歌控件真是好用。能用list還是list方便。

          六、加載頁面,保存Cookie

          啟動之后的頁面主要是為了登錄賬號,因為大部分的API是需要登錄之后才可以獲取到的。

          1.webview加載頁面

          在base/layout/ability_main.xml中添加webview組件,代碼如下

              <ohos.agp.components.webengine.WebView
                  ohos:id="$+id:webview"
                  ohos:height="match_parent"
                  ohos:width="match_parent">
              </ohos.agp.components.webengine.WebView>

          然后在啟動頁面執行加載操作。但其實加載前需要先從數據庫中提取cookie信息,這個接下來說。

          String url="https://m.bilibili.com";
          WebView webView=(WebView) findComponentById(ResourceTable.Id_webview);
          webView.getWebConfig().setJavaScriptPermit(true);  // 如果網頁需要使用JavaScript,增加此行;如何使用JavaScript下文有詳細介紹
          webView.load(url);

          2.Cookie的讀取和保存類

          com/liangzili/demos/utils/CookieUtils.java

          public class CookieUtils {
              private static final HiLogLabel TAG=new HiLogLabel(HiLog.DEBUG,0x0,CookieUtils.class.getName());
          
              /**
               * 使用關系型數據庫[讀取]Cookie
               * @param preferences
               * @param url
               */
              public static void ExtarctCookie(Preferences preferences, String url){
                  Map<String, ?> map=new HashMap<>();
                  //先從數據庫中取出cookie
                  map=PreferenceDataBase.GetCookieMap(preferences);
                  //然后寫入到cookieStore
                  CookieStore cookieStore=CookieStore.getInstance();//1.獲取一個CookieStore的示例
                  for (Map.Entry<String, ?> entry : map.entrySet()) {
                      HiLog.info(TAG,entry.getKey()+"="+entry.getValue().toString());
                      cookieStore.setCookie(url,entry.getKey()+"="+entry.getValue().toString());//2.寫入數據,只能一條一條寫
                  }
              }
              
              /**
               * 使用關系型數據庫[保存]Cookie
               * @param preferences  數據庫的Preferences實例
               * @param url  指定Cookie對應的域名
               */
              public static void SaveCookie(Preferences preferences,String url){
                  //先取出要保存的cookie
                  CookieStore cookieStore=CookieStore.getInstance();
                  String cookieStr=cookieStore.getCookie(url);
                  HiLog.info(TAG,"saveCookie(String url)"+url+cookieStr);
          
                  //然后將cooke轉成map
                  Map<String,String> cookieMap=cookieToMap(cookieStr);
          
                  //最后將map寫入數據庫
                  PreferenceDataBase.SaveMap(preferences,cookieMap);
              }
              // cookieToMap
              public static Map<String,String> cookieToMap(String value) {
                  Map<String, String> map=new HashMap<String, String>();
                  value=value.replace(" ", "");
                  if (value.contains(";")) {
                      String values[]=value.split(";");
                      for (String val : values) {
                          String vals[]=val.split("=");
                          map.put(vals[0], vals[1]);
                      }
                  } else {
                      String values[]=value.split("=");
                      map.put(values[0], values[1]);
                  }
                  return map;
              }
          }

          七、偏好型數據庫

          數據庫的操作主要是com/liangzili/demos/database/PreferenceDataBase.java 這個類。使用輕量級偏好型數據庫,更符合我們這里的需求。

          1.獲取Preferences實例

          public class PreferenceDataBase {
              private static final HiLogLabel TAG=new HiLogLabel(HiLog.DEBUG,0x0,PreferenceDataBase.class.getName());
          
              /**
               * 獲取Preferences實例
               * @param context  數據庫文件將存儲在由context上下文指定的目錄里。
               * @param name  fileName表示文件名,其取值不能為空,也不能包含路徑
               * @return  //返回對應數據庫的Preferences實例
               */
              public static Preferences register(Context context,String name) {
                  DatabaseHelper databaseHelper=new DatabaseHelper(context);
                  Preferences preferences=databaseHelper.getPreferences(name);
                  return preferences;
              }
              ... ...
          }

          2.從數據庫中保存和讀取Map

              /**
               * Map[保存]到偏好型數據庫
               * @param preferences  數據庫的Preferences實例
               * @param map  要保存的map
               */
              public static void SaveMap(Preferences preferences,Map<String,String> map){
                  // 遍歷map
                  for (Map.Entry<String, String> entry : map.entrySet()) {
                      HiLog.info(TAG,entry.getKey() + "=" + entry.getValue());
                      preferences.putString(entry.getKey(),entry.getValue());//3.將數據寫入Preferences實例,
                  }
                  preferences.flushSync();//4.通過flush()或者flushSync()將Preferences實例持久化。
              }
          
              /**
               *  從偏好型數據庫[讀取]Map
               * @param preferences  數據庫的Preferences實例
               * @return  要讀取的map
               */
              public static Map<String,?> GetCookieMap(Preferences preferences){
                  Map<String, ?> map=new HashMap<>();
                  map=preferences.getAll();//3.讀取數據
                  return map;
              }

          3.提取某些Cookie的值

              /**
               * 獲取Cookie中的SESSDATA值
               * @param context 上下文用來指定數據文件存儲路徑
               * @return  Cookie中的SESSDATA值
               */
              public static String getSessData(Context context){
                  // 開啟數據庫
                  DatabaseHelper databaseHelper=new DatabaseHelper(context);//1.創建數據庫使用數據庫操作的輔助類
                  Preferences preferences=databaseHelper.getPreferences("bilibili");//2.獲取到對應文件名的Preferences實例,filename是String類型
                  String SESSDATA=preferences.getString("SESSDATA","");    //3.讀取數據
                  return SESSDATA;
              }
          
              /**
               * 獲取Cookie中的Vmid值
               * @param context
               * @return Cookie中的Vmid值
               */
              public static String getVmid(Context context){
                  // 開啟數據庫
                  DatabaseHelper databaseHelper=new DatabaseHelper(context);//1.創建數據庫使用數據庫操作的輔助類
                  Preferences preferences=databaseHelper.getPreferences("bilibili");//2.獲取到對應文件名的Preferences實例,filename是String類型
                  String DedeUserID=preferences.getString("DedeUserID","");    //3.讀取數據
                  return DedeUserID;
              }



          ——————

          原創:老王丨鴻蒙hms開發者高級認證持證人!學習更多鴻蒙OS相關開發技術可以關注我的公眾號:鴻蒙開發者老王

          論文最苦惱的事情是什么?

          到處找資料、到處找數據!

          到哪找文獻資料?這個問題我已經在之前幫你們解決啦!

          詳情看這篇文章:

          大學資料、文獻查找、畢業論文?還在苦惱嗎?這幾個網站足夠使用

          那到哪里去找統計數據呢?又該怎樣查重呢?

          要知道寫論文嘛,動不動就得來一句:

          根據調查,中國2020年......相比往年......

          那今天,我們今天就來推薦幾個有著超全統計數據的網站!并且介紹些寫論文所需要的工具。


          一、大數據導航


          大數據導航

          網址:http://hao.199it.com/


          這個網站太厲害了,我要把它單獨放一類!

          這個網站之所以厲害,是因為它集合了近千個數據網站!包括大量統計國外、全球數據的網站!且對于每一類數據網站做了詳細分類,目前已有113個分類(如果我沒數錯的話),每個分類,少則四五個網站,多則上十個!



          比如,我找到了一個實時網絡攻擊地圖




          雖然我也不知道它是用來干什么的......

          但是,有了這個導航網站,還怕找不到數據嗎?

          同時,網站主頁還提供了幾十個熱門工具網站鏈接。



          往下滑,可以搜索你想要的數據網站。



          是不是很厲害?!!!

          而且我還發現,網站貼心的對付費網站做了標注enna~


          二、常用數據平臺


          1、政府開放數據平臺


          1、國家數據

          網址:http://data.stats.gov.cn/


          由中國國家統計局所提供的數據,包含了我國經濟民生教育等多個方面的資料,并且范圍覆蓋月、季、年三個方面,其它還有普查數據、地區數據、國際數據等,簡直不要太好用!


          其它可能用到的:


          1)國家知識產權局

          網址:http://www.sipo.gov.cn

          (提供相關專利查詢)


          2)中國海關

          網址:http://www.customs.gov.cn

          (中國進出口的相關數據)

          3)中國人民銀行

          網址:http://www.pbc.gov.cn/

          (關于中國金融市場的相關政策及數據)


          4)銀監會

          網址:http://www.cbrc.gov.cn

          (關于銀行金融等相關數據)


          5)中國證監會

          網址http://www.csrc.gov.cn

          (查找中國證券的相關信息)


          6)工業和信息化部

          網址:http://www.miit.gov.cn

          (大量的有關中國工業及信息化數據)
          再提供一個地區性政府間經濟合作組織的網站,就是大名鼎鼎的:


          7)經合組織(OECD)

          網址:https://data.oecd.org

          (里面有很多成員國相關的統計數據)


          還記得文章開頭提到的那個導航網站嗎?

          它里面提供了很多國外政府開放數據平臺,具體見下圖:(鏈接就不貼了,直接去文章最上面的那個網站找)



          2、國家數據及外國統計數據


          1)國家數據

          網址:http://data.stats.gov.cn/

          (這個剛剛介紹了,為了保持本部分內容完整性,把它拿出來再說一遍。)


          2)教育部

          網址:http://www.moe.gov.cn/jyb_sjzl/


          如果你想了解更多外國或國際教育數據,還是一樣,去文章最開始推薦的那個大數據導航網站找,貼個圖:



          3)農業部

          網址:http://zdscxx.moa.gov.cn/

          4)生態環境部

          網址:http://www.mee.gov.cn/hjzl/


          5)人力資源與社會保障部

          網址:http://www.mohrss.gov.cn/


          6)衛生健康委員會

          網址:http://www.nhc.gov.cn/wjw/xinx/xinxi.shtml


          如果你想知道其它國家統計數據,比如什么加拿大、韓國之類的,同樣,大數據導航網站里搜,貼個圖:



          保守估計有六十多個國家的統計網站吧


          3、學術類網站數據


          中國知網

          網址:https://cnki.net/


          萬丈數據知識服務平臺

          網址:http://www.wanfangdata.com.cn/index.html


          愛學術

          網址:https://www.ixueshu.com/


          這幾個文獻網站都提供專業的統計數據服務,相對來說,這些網站提供的數據使用頻率更高些。拿知網舉例,下圖藍框里的內容,就是知網提供的數據平臺:(其它網站同理)



          4、互聯網趨勢分析


          看:



          當然還是那個網站了( ̄▽ ̄)"

          網址:http://hao.199it.com/

          點擊后直接跳轉,方便吧!


          三、論文常用工具


          1、問卷調查工具


          這個我覺得,就用騰訊問卷啦,有微信小程序,問卷鏈接還可以直接轉到qq里,不管是填問卷的人,還是收集數據的人,都挺方便的。

          網址:https://wj.qq.com/


          如果你想用其它網站......



          篇幅原因,這里就不過多講了,以后有機會抽時間做下講解,直接看上圖。

          嗯,還是那個網站。。。


          2、論文查重具體流程


          論文查重,不管你使用哪種查重網站,最重要的是:

          以學校要求的查重系統為準!學校用什么,你就用什么!

          因為不同的平臺,它的論文庫和查重所使用的算法是不同的,所以用其它平臺進行論文查重,可能并沒有很大的參考價值!


          具體流程:

          1、在初稿完成后,去某寶購買一次查重服務。

          注意:

          (1)請選擇合規范的第三方店鋪。像知網,正規的商品訂單都是可以驗明真偽的,所以,如果訂單無法驗明真偽的話,不管別人再怎么說得天花亂墜,也不要相信!

          (一般查重一次的最低價也需要二三十左右,太過便宜的一定要慎重選擇,畢竟成本在那里)

          (2)不要經過他人之手查重。因為現在第三方泄密的例子真的不少。比如,你在某寶購買了某平臺的查重服務,一定不要讓客服代替你去查重,同時,查重的文檔也記得刪掉個人學校等相關信息。


          2、初稿查重完畢后,修改重復部分,再次購買查重服務,確保重復率在學校要求之下,同時預留一定的比例。(比如,學校要求百分之二十,那你就弄到百分之十五以下。


          3、使用學校提供的查重服務,做最后的查重。(一般會提供一到兩次機會)


          關注人質綜合,帶你了解更多有趣又實用的網站app!

          歡迎評論區留言哦!

          核桃真的能補腦嗎?(圖片來源:Pixabay)

          每天五分鐘,你就能提高以下能力:

          專注力、頭腦敏捷性、語言能力、協調能力、問題解決能力和記憶力,這是真的嗎?

          芥末堆 洛克 2016年8月12日報道

          某日,芥末堆編輯部的小梅正聚精會神地拿著手機點點劃劃,走近一看,原來他在玩手機游戲。

          面對大家呲之以鼻的態度,“這可不是普通的手機游戲,”小梅仿佛被冤枉的說,“這可是由專業的神經科學家和教育專家團隊所開發的大腦訓練游戲。”

          據了解,這是一款名為Peak的App,它曾受到蘋果公司App Store的推薦,在教育類app中排名第一。

          Penny同學告訴我,她一年前就下載了同類的腦力訓練App。在她的手機里,現在還有Lumosity,Fit Brains、Memorado等腦力訓練App。這類App其實大同小異,它們都提供多種類型的趣味游戲,宣傳能提升你的記憶力、注意力、問題解決能力、敏捷度、語言能力、協調力以及情緒控制能力。

          在國外,此類應用受到了用戶的歡迎,也得到了資本市場的關注。Lumosity稱其用戶已達7000萬,分布于全球182個國家。它于2012年完成了D輪3150萬美元的融資,總融資額已達7000萬美元。作為的后起之秀的Peak在2015年四月完成A輪融資,總融資額1000萬美元。來自德國的Memorado在2015年3月就已經有350萬用戶,Fit Brains更是有1800萬的下載量。

          就職于搜狐視頻的紅紅是Peak的忠實用戶。在第一次使用Peak時,它會讓你做一系列游戲任務,然后根據你的表現給你評分。接下來每天都會要求你完成若干游戲任務。紅紅玩這個游戲大約有一個月的時間,每天都會利用碎片時間,總計花一小時左右來完成當天的任務。對她來說,游戲本身十分有趣。而游戲成績的不斷上升也能給她帶來成就感。這些游戲本來是可以將成績分享到Facebook,不過本地化并沒有做好,目前還不支持將游戲成績分享到朋友圈和微博上。不過紅紅曾手動截屏然后分享到自己的朋友圈中。

          小梅告訴我,對他而言,玩此類游戲的重要原因,便是社會比較所帶來的優越感。經過一個多月,每天15分鐘的練習,小梅的游戲成績已經比游戲庫里的同齡人要高出不少。他看著自己“大師級”的游戲評價,笑得十分滿意。

          小梅的白框已經比同齡人的藍框要大了不少

          出色的游戲設計和其所宣稱的功效是此類App吸引用戶最主要的兩大因素。紅紅告訴我,她并沒有完全相信此類游戲所宣稱的提升腦力的效果。但她依然被其背后的神經科學光環所打動。Penny也是如此,一些人本來都會花點時間玩手機游戲,為什么不花時間玩這些同樣有趣但是宣稱能提升智力的教育游戲呢?每個人都希望變得更聰明,如果市場上真的出現某種能提升人類智力的食物、藥物、課程、游戲等產品,那一定會大受歡迎。

          App store中的腦力訓練類app

          在蘋果的App store中,編輯們將一些應用聚合在一起,稱之為“腦力訓練”。此類App大多是基礎版本免費,而要想玩更多的腦力訓練游戲,就需要用戶付費購買高級版本。小梅已經花了30元,買了一個月的高級版本。而紅紅則準備花數百元購買終身高級版本。

          這種所謂的“腦力訓練”游戲具體是什么樣的呢?有的游戲會在屏幕上呈現數字,有正數也有負數,要求你從小到大點擊這些數字。有的游戲會在屏幕上顯示一些方塊,然后這些方塊會消失,要求玩家點擊方塊曾經的位置。

          要求迅速從小到大點擊數字的游戲

          讓我們以Lumosity中的“Color Match”游戲為例進行深度的介紹。該游戲會給你呈現不同顏色的文字。比如綠色的“綠色”,紅色的“綠色”,黑色的“藍色”,黃色的“黃色”等等。它要求你又快又好地判斷字的顏色和含義是否相符。比如綠色的“綠色”便是相符,紅色的“綠色”便是不相符。

          有心理學相關背景的朋友應該馬上就會明白,這是一種典型的認知心理學實驗范式,叫做Stroop效應。當文字呈現在人類眼前時,對語義的信息加工和顏色的信息加工都會無意識地快速進行,而當兩者不符時,便會產生相互干擾。

          而這個“Color Match”游戲任務,會記錄你在單位時間內答對的題目次數,然后給出相應的分數。隨著你玩這個游戲的次數越來越多,你變得越來越熟練,取得的分數也越來越高。它宣稱,你玩這個游戲玩得越好,思維靈活性就越高。因為你玩這個游戲的分數提高了,所以它宣稱你的思維靈活性也得到了鍛煉,也提高了。

          表面看來,這話說得很有道理。但它其實只說對了一半。認知心理學家們的確會使用Stroop效應來測量人的認知抑制能力。因為Stroop效應中的成績和一個人的認知抑制能力有一定程度的正相關。認知抑制能力是一個專業術語,大家可以簡單地理解成,在別的條件不變的情況下,認知抑制能力越高,你就越聰明。

          可以這么說,如果大家事先都沒有刻意練習過這個Stroop效應的實驗。如果你在第一次玩這個“Color Match”中所獲得的分數如果比別人第一次玩要高,那么你的認知抑制能力就有較高可能性比別人高。但是,這不意味著大家都刻意地練習玩這個游戲,然后游戲分數提升之后,就能提升認知抑制能力。

          用一個十分恰當的比方,智商測驗可以用來估測一個人的聰明程度。大家都來參加智商測驗,如果你的分數比別人高,那么你很可能就比別人聰明。但是這不意味著,大家不斷地參加智商測驗,做此類智商測驗變得越來越熟練,獲得越來越高的分數,然后就意味著自己的智商也變得越來越高了。

          我們要區分兩種效應,一種叫“練習效應”,一種叫“遷移效應”。粗略地說,前者是指你能通過不斷地練習,來提升自己在某項測驗中的成績,而后者是指,你提升自己在某項測驗中的成績之后,在其他項目中的成績也能提高。很多測驗項目都具備練習效應,這很容易理解。但要具備遷移效應,就非常難。跑步是具備遷移效應的,你跑步成績提升之后,游泳成績甚至籃球成績都有相應的提升。但是智商測驗就沒有遷移效應。

          Lumosity自己的科學實驗室發布研究報告,稱Lumosity能有效的提升人類的認知能力。然而,Lumosity曾因為虛假宣傳被美國聯邦貿易委員會罰款兩百萬美元。因為在心理學界,科學家們的主流研究結果都認為,沒有任何證據能說明此類腦力訓練類app真的能提升人類的認知能力。因為找不到長期有效的實驗室外遷移效應。

          綜合來看,你玩這類游戲分數越來越高,僅僅意味著你玩這類游戲分數越來越高,并不意味著你變得更聰明了,你的工作績效不一定能上漲,你的考試成績也不一定會上升。

          1、本文是芥末堆網原創文章,轉載此文章請注明出處(芥末堆網)及本頁鏈接://www.jiemodui.com/N/60868.html。

          2、芥末堆不接受通過公關費、車馬費等任何形式發布失實文章,只呈現有價值的內容給讀者。

          3、如果你也從事教育,并希望被芥末堆報道,請發郵件到 tianyi.mei@jmdedu.com 告訴我們。

          來源: 芥末堆


          主站蜘蛛池模板: 精品无码一区二区三区水蜜桃| 性色A码一区二区三区天美传媒| 一区二区传媒有限公司| 免费无码一区二区三区蜜桃大| 国产精品第一区第27页| 国产在线步兵一区二区三区| 久久一本一区二区三区| 嫩B人妻精品一区二区三区| 蜜桃视频一区二区三区| 成人欧美一区二区三区在线视频| 久久免费区一区二区三波多野| 午夜肉伦伦影院久久精品免费看国产一区二区三区 | 国产午夜精品一区二区| 精产国品一区二区三产区| 亚洲码一区二区三区| 日韩精品无码一区二区三区不卡| 熟女大屁股白浆一区二区| 大伊香蕉精品一区视频在线| 一区二区三区四区在线播放 | 国产香蕉一区二区三区在线视频| 91精品一区二区三区在线观看| 麻豆天美国产一区在线播放| 精品亚洲A∨无码一区二区三区| 久久福利一区二区| 在线播放国产一区二区三区| 美女毛片一区二区三区四区| 亚洲狠狠狠一区二区三区| 国产精品久久久久久麻豆一区 | 久久久99精品一区二区| 在线观看国产一区| 亚洲AV本道一区二区三区四区| 99精品一区二区免费视频| 99精品国产一区二区三区2021 | 成人精品一区二区三区电影| 深田咏美AV一区二区三区| 日韩免费一区二区三区| 午夜精品一区二区三区在线视| 免费观看一区二区三区| 免费无码一区二区三区| 国产乱码精品一区二区三区四川人| 最新中文字幕一区二区乱码|