首页 文章 精选 留言 我的

精选列表

搜索[安卓系统],共10021篇文章
优秀的个人博客,低调大师

安卓运动圆环自定义View

运动圆环自定义View.gif 记得这个东西原来有个同事问过我,当时正在自学自定义View和属性动画这一块,对这个功能也没有写过,所以就google了一下,发了几个类似效果的github项目给朋友,今天礼拜天难得有心情写写代码,所以想想实现一下这个自定义View的效果。 首先,我们从这个gif的效果图中就可以得知这个自定义View我们需要哪些自定义属性,内部圆环的颜色、外部圆环的颜色、圆环的宽度、字体的大小、颜色,话不多说,直接撸码。 <!-- 运动圆环自定义属性 --> <declare-styleable name="MotionCrcle"> <attr name="outerCrcleColor" format="color"></attr> <attr name="innerCrcleColor" format="color"></attr> <attr name="crcleTextColor" format="color"></attr> <attr name="crcleTextSize" format="integer"></attr> <attr name="crcleWidth" format="integer"></attr> </declare-styleable> /** * Created by Administrator on 2018/5/13. * 运动圆环自定义View */ public class MotionCrcle extends View { /** * 外部圆环颜色 */ private int mOuterColor = Color.BLUE; /** * 里面圆环颜色 */ private int mInnerColor = Color.RED; /** * 跑步数的文字大小 */ private int mTextSize = 30; /** * 跑步文字的颜色 */ private int mTextColor = Color.BLACK; /** * 圆环的宽度 */ private int mCrcleWidth = 45; private Paint mOuterPaint, mInnerPaint, mTextPaint; private float mMaxSteps = 0; private int mCurrentSteps = 0; public MotionCrcle(Context context) { this(context, null); } public MotionCrcle(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public MotionCrcle(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MotionCrcle); mOuterColor = array.getColor(R.styleable.MotionCrcle_outerCrcleColor, mOuterColor); mInnerColor = array.getColor(R.styleable.MotionCrcle_innerCrcleColor, mInnerColor); mCrcleWidth = array.getInt(R.styleable.MotionCrcle_crcleWidth, mCrcleWidth); mTextColor = array.getColor(R.styleable.MotionCrcle_crcleTextColor, mTextColor); mTextSize = array.getInt(R.styleable.MotionCrcle_crcleTextSize, mTextSize); initPaint(); array.recycle(); } public synchronized void setmMaxSteps(float mMaxSteps) { this.mMaxSteps = mMaxSteps; } public synchronized void setmCurrentSteps(int mCurrentSteps) { this.mCurrentSteps = mCurrentSteps; invalidate(); } private void initPaint() { mOuterPaint = new Paint(); mOuterPaint.setAntiAlias(true); mOuterPaint.setStrokeWidth(mCrcleWidth); mOuterPaint.setStrokeCap(Paint.Cap.ROUND); mOuterPaint.setStyle(Paint.Style.STROKE); mOuterPaint.setColor(mOuterColor); mInnerPaint = new Paint(); mInnerPaint.setAntiAlias(true); mInnerPaint.setStrokeWidth(mCrcleWidth); mInnerPaint.setStrokeCap(Paint.Cap.ROUND); mInnerPaint.setStyle(Paint.Style.STROKE); mInnerPaint.setColor(mInnerColor); mTextPaint = new Paint(); mTextPaint.setAntiAlias(true); mTextPaint.setTextSize(mTextSize); mTextPaint.setColor(mTextColor); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); if(width != height){ /** * 因为运动圆环是一个正方形,所以我们要设置一个最小值作为View的长度 */ int min = Math.min(width, height); widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, MeasureSpec.EXACTLY); heightMeasureSpec = MeasureSpec.makeMeasureSpec(min, MeasureSpec.EXACTLY); setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); } } @Override protected void onDraw(Canvas canvas) { //因为圆环本身是具有宽度的,所以我们这里左边的顶点要往右mCrcleWidth个距离,右边底部的点要往左,所以要减去mCrcleWidth RectF recf = new RectF(mCrcleWidth, mCrcleWidth, getWidth() - mCrcleWidth, getHeight() - mCrcleWidth); //135是外部圆环的初始角度,270是所画的总角度数 canvas.drawArc(recf, 135, 270, false, mOuterPaint); if(mCurrentSteps == 0){ return; } //算出当前步数是最大步数的比值 float angle = mCurrentSteps / mMaxSteps; canvas.drawArc(recf, 135, 270 * angle, false, mInnerPaint); String stepText = mCurrentSteps+""; Rect bounds = new Rect(); //算出步数文字的baseLine,也就是基准线 mTextPaint.getTextBounds(stepText, 0, stepText.length(), bounds); int x = getWidth() / 2 - bounds.width() / 2; Paint.FontMetricsInt metrices = mTextPaint.getFontMetricsInt(); int diffY = (metrices.bottom - metrices.top) / 2 - metrices.bottom; int baseLine = getHeight() / 2 + diffY; canvas.drawText(stepText, x, baseLine, mTextPaint); } } 文字基准线说明图.jpg 文字的绘制和画圆画弧不一样,其实仔细想想也明白,如果绘制按照左上角开始的话是不现实的,因为文字不可能是简单的顶部或底部对其,应该是重心对齐,简单说就是基准线,所以代码中基准线的算法是bottom-top再除以2减去bottom,以基准线开始绘制,top就是负数,bottom是正数。 public class MainActivity extends AppCompatActivity { private Button mBtn; private baohuo.rzj.com.myapplication.Views.MotionCrcle mCrcle; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mBtn = (Button) findViewById(R.id.btn); mCrcle = (MotionCrcle) findViewById(R.id.motion_crcle); mCrcle.setmMaxSteps(10000); final ValueAnimator animator = ObjectAnimator.ofFloat(0,4500); animator.setDuration(2000); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { float value = (float) valueAnimator.getAnimatedValue(); mCrcle.setmCurrentSteps((int) value); } }); mBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { animator.start(); } }); } } 以上是Activity的代码,非常简单,不做解释,这个值得一提的是,我在写的时候内部圆环一直没有绘制,文字在不断变化,通过debug发现,我把步数最大值和当前步数设置为int,一个小的int除以大的int的到的只有0,所以我把最大步数改为float,这样得出的值就有小数位,最后附上xml代码。 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="baohuo.rzj.com.myapplication.MainActivity"> <Button android:id="@+id/btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="30dp" android:text="运动圆环开启" android:gravity="center"/> <baohuo.rzj.com.myapplication.Views.MotionCrcle android:id="@+id/motion_crcle" android:layout_below="@+id/btn" android:layout_width="200dp" android:layout_height="200dp" app:outerCrcleColor="@color/colorPrimary" app:innerCrcleColor="@color/colorAccent" app:crcleTextColor="@color/colorPrimaryDark" app:crcleTextSize="40" app:crcleWidth="20"/> </RelativeLayout>

优秀的个人博客,低调大师

安卓应用安全指南 4.9 使用`WebView`

4.9 使用WebView 原书:Android Application Secure Design/Secure Coding Guidebook 译者:飞龙 协议:CC BY-NC-SA 4.0 WebView使你的应用能够集成 HTML / JavaScript 内容。 4.9.1 示例代码 我们需要采取适当的行动,具体取决于我们想通过WebView展示的内容,尽管我们可以通过它轻松展示网站和 html 文件。 而且我们还需要考虑来自WebView卓越功能的风险; 如 JavaScript-Java 对象绑定。 我们特别需要关注 JavaScript。 (请注意 JavaScript 默认是禁用的,我们可以通过WebSettings#setJavaScriptEnabled()来启用它。 启用 JavaScript 存在潜在的风险,即恶意第三方可以获取设备信息并操作设备。 以下是使用WebView [19] 的应用的原则: [19] 严格地说,如果我们可以说内容是安全的,你可以启用 JavaScript。 如果内容是在内部管理的,则内容应该保证安全。 公司可以保护他们。 换句话说,我们需要让企业代表的决策,来为其他公司的内容启用 JavaScript。 由可信伙伴开发的内容可能会有安全保证。 但仍有潜在风险。 因此,负责人需要作出决定。 (1)如果应用使用内部管理的内容,则可以启用 JavaScript。 (2)除上述情况外,你不应启用 JavaScript。 图 4.9-1 显示了根据内容特征选择示例代码的流程图。 4.9.1.1 仅显示存储在 APK 中的assets / res目录下的内容 如果你的应用仅显示存储在 apk 中assets/和res/目录下的内容,则可以启用 JavaScript。 以下示例代码展示了,如何使用WebView显示存储在assets/和res/下的内容。 要点: 1) 禁止访问文件(apk 文件中的assets/和res/下的文件除外)。 2) 你可以启用 JavaScript。 WebViewAssetsActivity.java package org.jssec.webview.assets; import android.app.Activity; import android.os.Bundle; import android.webkit.WebSettings; import android.webkit.WebView; public class WebViewAssetsActivity extends Activity { /** * Show contents in assets */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); WebView webView = (WebView) findViewById(R.id.webView); WebSettings webSettings = webView.getSettings(); // *** POINT 1 *** Disable to access files (except files under assets/ and res/ in this apk) webSettings.setAllowFileAccess(false); // *** POINT 2 *** Enable JavaScript (Optional) webSettings.setJavaScriptEnabled(true); // Show contents which were stored under assets/ in this apk webView.loadUrl("file:///android_asset/sample/index.html"); } } 4.9.1.2 仅显示内部管理的内容 只有当你的网络服务和你的 Android 应用可以采取适当措施来保护它们时,你才可以启用 JavaScript 来仅仅显示内部管理的内容。 Web 服务端操作:如图 4.9-2 所示,你的 Web 服务只能引用内部管理的内容。 另外,Web 服务需要采取适当的安全措施。 因为你的网络服务涉及的内容可能存在风险,因此存在潜在风险;如恶意攻击代码注入,数据操作等。请参阅“4.9.2.1 仅在内容由内部管理时启用 JavaScript(必需)”。 Android 应用端操作:使用 HTTPS,应用只有在证书可信的情况下,才应与受管理的 Web 服务建立网络连接。 以下示例代码是一个活动,展示了内部管理的内容。 要点: 1) 适当处理来自WebView的 SSL 错误。 2) (可选)启用WebView的 JavaScript。 3) 将 URL 限制为 HTTPS 协议。 4) 将 URL 限制在内部。 WebViewTrustedContentsActivity.java package org.jssec.webview.trustedcontents; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.net.http.SslCertificate; import android.net.http.SslError; import android.os.Bundle; import android.webkit.SslErrorHandler; import android.webkit.WebView; import android.webkit.WebViewClient; import java.text.SimpleDateFormat; public class WebViewTrustedContentsActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); WebView webView = (WebView) findViewById(R.id.webView); webView.setWebViewClient(new WebViewClient() { @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { // *** POINT 1 *** Handle SSL error from WebView appropriately // Show SSL error dialog. AlertDialog dialog = createSslErrorDialog(error); dialog.show(); // *** POINT 1 *** Handle SSL error from WebView appropriately // Abort connection in case of SSL error // Since, there may be some defects in a certificate like expiration of validity, // or it may be man-in-the-middle attack. handler.cancel(); } }); // *** POINT 2 *** Enable JavaScript (optional) // in case to show contents which are managed in house. webView.getSettings().setJavaScriptEnabled(true); // *** POINT 3 *** Restrict URLs to HTTPS protocol only // *** POINT 4 *** Restrict URLs to in-house webView.loadUrl("https://url.to.your.contents/"); } private AlertDialog createSslErrorDialog(SslError error) { // Error message to show in this dialog String errorMsg = createErrorMessage(error); // Handler for OK button DialogInterface.OnClickListener onClickOk = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { setResult(RESULT_OK); } }; // Create a dialog AlertDialog dialog = new AlertDialog.Builder( WebViewTrustedContentsActivity.this).setTitle("SSL connection error") .setMessage(errorMsg).setPositiveButton("OK", onClickOk) .create(); return dialog; } private String createErrorMessage(SslError error) { SslCertificate cert = error.getCertificate(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); StringBuilder result = new StringBuilder() .append("The site's certification is NOT valid. Connection was disconnected.¥n¥nError:¥n"); switch (error.getPrimaryError()) { case SslError.SSL_EXPIRED: result.append("The certificate is no longer valid.¥n¥nThe expiration date is ") .append(dateFormat.format(cert.getValidNotAfterDate())); return result.toString(); case SslError.SSL_IDMISMATCH: result.append("Host name doesn't match. ¥n¥nCN=") .append(cert.getIssuedTo().getCName()); return result.toString(); case SslError.SSL_NOTYETVALID: result.append("The certificate isn't valid yet.¥n¥nIt will be valid from ") .append(dateFormat.format(cert.getValidNotBeforeDate())); return result.toString(); case SslError.SSL_UNTRUSTED: result.append("Certificate Authority which issued the certificate is not reliable.¥n¥nCertificate Authority¥n") .append(cert.getIssuedBy().getDName()); return result.toString(); default: result.append("Unknown error occured. "); return result.toString(); } } } 4.9.1.3 显示非内部管理的内容 如果你的应用显示的内容不在内部管理,请勿启用 JavaScript,因为存在访问恶意内容的潜在风险。 以下示例代码是显示非内部管理的内容的活动。 此示例代码显示由用户通过地址栏输入的 URL 指定的内容。 请注意,当 JavaScript 错误发生时,JavaScript 被禁用并且连接中止。 HTTPS 通信的详细信息,错误处理与“4.9.1.2 仅显示内部管理的内容”相同。 详细信息请参阅“5.4 通过 HTTPS 进行通信”。 要点: 1) 适当处理来自WebView的 SSL 错误。 2) 禁用WebView的 JavaScript。 WebViewUntrustActivity.java package org.jssec.webview.untrust; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.graphics.Bitmap; import android.net.http.SslCertificate; import android.net.http.SslError; import android.os.Bundle; import android.view.View; import android.webkit.SslErrorHandler; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.Button; import android.widget.EditText; import java.text.SimpleDateFormat; public class WebViewUntrustActivity extends Activity { /* * Show contents which are NOT managed in-house (Sample program works as a simple browser) */ private EditText textUrl; private Button buttonGo; private WebView webView; // Activity definition to handle any URL request private class WebViewUnlimitedClient extends WebViewClient { @Override public boolean shouldOverrideUrlLoading(WebView webView, String url) { webView.loadUrl(url); textUrl.setText(url); return true; } // Start reading Web page @Override public void onPageStarted(WebView webview, String url, Bitmap favicon) { buttonGo.setEnabled(false); textUrl.setText(url); } // Show SSL error dialog // And abort connection. @Override public void onReceivedSslError(WebView webview, SslErrorHandler handler, SslError error) { // *** POINT 1 *** Handle SSL error from WebView appropriately AlertDialog errorDialog = createSslErrorDialog(error); errorDialog.show(); handler.cancel(); textUrl.setText(webview.getUrl()); buttonGo.setEnabled(true); } // After loading Web page, show the URL in EditText. @Override public void onPageFinished(WebView webview, String url) { textUrl.setText(url); buttonGo.setEnabled(true); } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); webView = (WebView) findViewById(R.id.webview); webView.setWebViewClient(new WebViewUnlimitedClient()); // *** POINT 2 *** Disable JavaScript of WebView // Explicitly disable JavaScript even though it is disabled by default. webView.getSettings().setJavaScriptEnabled(false); webView.loadUrl(getString(R.string.texturl)); textUrl = (EditText) findViewById(R.id.texturl); buttonGo = (Button) findViewById(R.id.go); } public void onClickButtonGo(View v) { webView.loadUrl(textUrl.getText().toString()); } private AlertDialog createSslErrorDialog(SslError error) { // Error message to show in this dialog String errorMsg = createErrorMessage(error); // Handler for OK button DialogInterface.OnClickListener onClickOk = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { setResult(RESULT_OK); } }; // Create a dialog AlertDialog dialog = new AlertDialog.Builder( WebViewUntrustActivity.this).setTitle("SSL connection error") .setMessage(errorMsg).setPositiveButton("OK", onClickOk) .create(); return dialog; } private String createErrorMessage(SslError error) { SslCertificate cert = error.getCertificate(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); StringBuilder result = new StringBuilder() .append("The site's certification is NOT valid. Connection was disconnected.¥n¥nError:¥n"); switch (error.getPrimaryError()) { case SslError.SSL_EXPIRED: result.append("The certificate is no longer valid.¥n¥nThe expiration date is ") .append(dateFormat.format(cert.getValidNotAfterDate())); return result.toString(); case SslError.SSL_IDMISMATCH: result.append("Host name doesn't match. ¥n¥nCN=") .append(cert.getIssuedTo().getCName()); return result.toString(); case SslError.SSL_NOTYETVALID: result.append("The certificate isn't valid yet.¥n¥nIt will be valid from ") .append(dateFormat.format(cert.getValidNotBeforeDate())); return result.toString(); case SslError.SSL_UNTRUSTED: result.append("Certificate Authority which issued the certificate is not reliable.¥n¥nCertificate Authority¥n") .append(cert.getIssuedBy().getDName()); return result.toString(); default: result.append("Unknown error occured. "); return result.toString(); } } } 4.9.2 规则书 当你需要时候WebView时,遵循下列规则: 4.9.2.1 只在内容由内部管理时启用 JavaScript(必需) 对于WebView我们需要关注的是是否启用 JavaScript。 原则上,只有当应用访问内部管理的服务时,我们才能启用 JavaScript。 如果有可能访问非内部管理的服务,则不得启用 JavaScript。 内部管理的服务 如果应用访问内部开发的内容,并通过内部管理的服务器分发,我们可以说这些内容仅由贵公司修改。 另外,每个内容还需要仅仅引用存储在服务器中的内容,它们具有适当安全性。 在这种情况下,我们可以在WebView上启用 JavaScript。 请参阅“4.9.1.2 仅显示内部管理的内容”。 如果你的应用仅显示存储在 apk 中assets/和res/目录下的内容,你也可以启用 JavaScript。 请参阅“4.9.1.1 仅显示存储在assets / res目录下的内容”。 非内部管理的服务 你绝不能认为,你可以确保非内部管理的内容的安全性。 因此你必须禁用 JavaScript。 请参阅“4.9.1.3 显示非内部管理的内容”。 另外,如果内容存储在外部存储介质中,如 microSD,则必须禁用 JavaScript;因为其他应用可以修改内容。 4.9.2.2 使用 HTTPS 与内部管理的服务器进行通信(必需) 你必须使用 HTTPS 与内部管理的服务器通信,因为存在恶意第三方欺骗服务的潜在风险。 请参阅“4.9.2.4 正确处理 SSL 错误(必需)”和“5.4 通过 HTTPS 通信”。 4.9.2.3 禁用 JavaScript 来显示通过意图接收的 URL(必需) 如果你的应用需要显示从其他应用,以意图等形式传递的 URL,则不要启用 JavaScript。因为存在用恶意 JavaScript 显示恶意网页的潜在风险。 “4.9.1.2 仅显示内部管理的内容”部分中的示例代码,使用固定值 URL 显示内部管理的内容来确保安全。 如果你需要显示从意图收到的 URL,则必须确认该 URL 在内部管理的 URL 中。 简而言之,应用必须使用正则表达式等白名单来检查 URL。另外,它应该是 HTTPS。 4.9.2.4 适当处理 SSL 错误(必需) 当 HTTPS 通信发生 SSL 错误时,你必须终止网络通信并通知用户错误。 SSL 错误显示了无效的服务器认证风险或 MTIM(中间人攻击)风险。 请注意,WebView没有 SSL 错误的错误通知机制。 因此,你的应用必须显示错误通知,来向用户通知风险。 请参阅“4.9.1.2 仅显示内部管理的内容”和“4.9.1.3 显示非内部管理的内容”一节中的示例代码。 另外,你的应用必须终止带有错误通知的通信。 换句话说,你不可以这样做。 忽略错误来与服务保持通信。 重试 HTTP 通信而不是 HTTPS。 请参阅“5.4 通过 HTTPS 进行通信”中所述的详细信息。 WebView的默认行为是,发生 SSL 错误时终止通信。 因此,我们需要添加显示 SSL 错误通知。 然后我们可以正确处理 SSL 错误。 4.9.3 高级话题 4.9.3.1 Android 4.1 或更低版本中由addJavascriptInterface()引起的漏洞 4.2(API Level 17)版本以下的 Android,具有由addJavascriptInterface()引起的漏洞,这可能允许攻击者通过WebView上的 JavaScript 调用 Android 本地方法(Java)。 正如“4.9.2.1 只在内容由内部管理时启用 JavaScript”中所述,如果服务可以访问内部控制之外的服务,则不得启用 JavaScript。 在 Android 4.2(API Level 17)或更高版本中,已采取措施,将漏洞限制为在 Java 源代码上使用@JavascriptInterface注释的方法,而不是所有注入的 Java 对象的方法。 但是,如果服务可以访问内部控制之外的服务,则必须禁用JavaScript,像“4.9.2.1”中提到的那样。 4.9.3.2 由文件模式导致的问题 如果使用默认设置的WebView,应用具有访问权限的所有文件,都可以通过在网页中通过文件模式访问,而无论页面的来源如何。 例如,恶意网页可以通过使用文件模式,向应用的私有文件的 URI 发送请求,来访问存储在应用私有目录中的文件。 如果服务可以访问内部控制之外的服务,则禁用 JavaScript 的方法如“4.9.2.1 只在内容由内部管理时启用 JavaScript(必需)”中所述。 这样做是为了防止发送恶意文件模式请求。 同样在 Android 4.1(API Level 16)或更高版本的情况下,可以使用setAllowFileAccessFromFileURLs()和setAllowUniversalAccessFromFileURLs()来限制通过文件模式的访问。 禁用文件模式 webView = (WebView) findViewById(R.id.webview); webView.setWebViewClient(new WebViewUnlimitedClient()); WebSettings settings = webView.getSettings(); settings.setAllowUniversalAccessFromFileURLs(false); settings.setAllowFileAccessFromFileURLs(false); 4.9.3.3 使用 Web 消息时指定发送者的来源 Android 6.0(API Level 23)增加了一个 API,用于实现 HTML5 Web 消息传送。 Web 消息传送是一种在 HTML5 中定义的框架,用于在不同的浏览上下文之间,发送和接收数据 [20]。添加到WebView类的postWebMessage()方法是一种方法,通过 Web 消息传送定义的跨域消息传送协议处理数据传输。 [20] http://www.w3.org/TR/webmessaging/ 此方法从WebView已读入的浏览上下文中发送一个消息,该消息由其第一个参数指定; 然而,在这种情况下,有必要指定发送者的来源作为第二个参数。 如果指定的来源 [21] 与发送者上下文中的来源不符,则不会发送该消息。 通过以这种方式限制发送者来源,此机制旨在防止消息传递给非预期发送者。 [21] “来源”是一个 URL 模式以及一个主机名和端口号。 详细定义请参阅 http://tools.ietf.org/html/rfc6454。 但是,重要的是要注意,通配符可能被指定为postWebMessage()方法中的来源 [22]。如果指定了通配符,则不会检查消息的发送者来源,并且可以从任意来源发送消息。 在恶意内容已被读入WebView的情况下,如果发送重要消息时没有来源限制,则可能导致各种类型的损害。 因此,在使用WebView进行 Web 消息传递时,最好在postWebMessage()方法中明确指定特定的源。 [22] 请注意,通配符是Uri.EMPTY和Uri.parse("")(在编写 2016 年 9 月 1 日的版本时)。

优秀的个人博客,低调大师

Python之调用安卓(Android)ADB指令

作为开发或者是测试人员,或者是使用Python的伙伴,都会接触到ADB指令,下面就针对这种情况,来解析下Python调用ADB指令的操作。 一、长时间按某个元素:adb input shell ... 语法:adb -s 4d0041be98b01f shell input touchscreen swipe 540 716 545 718 1000 语义:-s 后跟设备号,swipe 先传移动坐标范围‘540 716 545 718’,然后1000是长按时间,单位毫秒。 实现代码 二、adb 命令输入中文: adb -s 设备号 shell input text 输入的内容 语法:adb shell am broadcast -a ADB_INPUT_TEXT --es msg "内容" 语义:利用“ADBKeyBoard”输入法来进行中文输入,通过广播的方式达到输入中文字符 三、adb 启动应用 语法:adb shell am start -n packgage名 /.activity 如启动计算器程序:adb shell am start -n com.android.calculator2/.Calculator 提醒点:一定要找到、找对activity和package 四、adb 查询短信、发短信消息 发短信语法:adb shell am start -a android.intent.action.SENDTO -d smsto:发送号码 --es sms_body 短信内容 查询短信消息:1.手机需ROOT;2.需找到短信数据库文件 数据库文件路径:'/data/data/com.android.providers.telephony/databases/mmssms.db',其中‘mmssms.db’文件就是保存短信内容的数据库文件,可利用正则表达式匹配。 说了这么多ADB指令,可发现adb指令是逐条运行的,那再Python中是一次性完成所有的步骤的,如下指令 语法:首先要确认设备是否已经ROOT ,指令:adb shell su -c cd 'data\system' 语义:-c 后面跟着具体的操作指令即可。 ADB

优秀的个人博客,低调大师

安卓App和java通信实例

服务器:放在电脑上运行的java文件 import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class MyServer implements Runnable{ //服务器连接 public static ServerSocket serverSocket; //连接 public static Socket socket; //端口 public static final int PORT = 8888; public void run() { DataInputStream dis = null; DataOutputStream dos = null; try { serverSocket = new ServerSocket(PORT); System.out.println("正在等待客户端连接..."); //这里处于等待状态,如果没有客户端连接,程序不会向下执行 while(true){ socket = serverSocket.accept(); dis = new DataInputStream(socket.getInputStream()); dos = new DataOutputStream(socket.getOutputStream()); //读取数据 String clientStr = dis.readUTF(); //写出数据 dos.writeUTF(clientStr); System.out.println("----客户端已成功连接!----"); //得到客户端的IP System.out.println("客户端的IP =" + socket.getInetAddress()); //得到客户端的端口号 System.out.println("客户端的端口号 =" + socket.getPort()); //得到本地端口号 System.out.println("本地服务器端口号=" + socket.getLocalPort()); System.out.println("-----------------------"); System.out.println("客户端:" + clientStr); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally {//我们把流的关闭写在finally里,即使读写出现问题,我们也能正常的关闭流! try { if (dis != null) dis.close(); if (dos != null) dos.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public static void main(String[] args){ Thread desktopServerThread = new Thread(new MyServer()); desktopServerThread.start(); } } App工程文件: 1、AndroidManifest.xml(主xml文件) <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.himi" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".MainActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> <uses-sdk android:minSdkVersion="4" /> <uses-permission android:name="android.permission.INTERNET" /> </manifest> 2、res/values/strings.xml(资源文件) <?xml version="1.0" encoding="utf-8"?> <resources> <string name="hello">这里输入文字发给服务器</string> <string name="app_name">SocketConnect</string> <string name="send">发送</string> <string name="get">这里显示服务器发来的信息!</string> </resources> 3、res/layout/main.xml(资源文件) <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> <EditText android:id="@+id/edit" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <Button android:id="@+id/Btn_commit" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/send" /> <TextView android:layout_width="fill_parent" android:id="@+id/tv" android:layout_height="wrap_content" android:text="@string/get" /> </LinearLayout> 4、src/com.himi/MainActivity.java(Activity文件,客户端端主程序) package com.himi; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; /** * @author Himi */ public class MainActivity extends Activity implements OnClickListener { private Button btn_ok; private EditText edit; private TextView tv; //Socket用于连接服务器获取输入输出流 private Socket socket; //服务器server/IP地址 private final String ADDRESS = "10.203.8.167"; //服务器端口 private final int PORT = 8888; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); this.requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.main); btn_ok = (Button) findViewById(R.id.Btn_commit); tv = (TextView) findViewById(R.id.tv); edit = (EditText) findViewById(R.id.edit); btn_ok.setOnClickListener(this); } public void onClick(View v) { if (v == btn_ok) { DataInputStream dis = null; DataOutputStream dos = null; try { //阻塞函数,正常连接后才会向下继续执行 socket = new Socket(ADDRESS, PORT); //socket = new Socket("localhost", PORT); dis = new DataInputStream(socket.getInputStream()); dos = new DataOutputStream(socket.getOutputStream()); //向服务器写数据 dos.writeUTF(edit.getText().toString()); String temp = "I say:"; temp += edit.getText().toString(); temp += "\n"; temp += "Server say:"; //读取服务器发来的数据 temp += dis.readUTF(); tv.setText(temp); } catch (IOException e) { Log.e("Himi", "Stream error!"); e.printStackTrace(); } finally { try { if (dis != null) dis.close(); if (dos != null) dos.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } } 本文转自beautifulzzzz博客园博客,原文链接: http://www.cnblogs.com/zjutlitao/p/3477529.html ,如需转载请自行联系原作者

优秀的个人博客,低调大师

安卓平台上的依赖注入(一)

刚开始学习软件工程的时候,我们经常会碰到像这样的事情: 软件应该符合 SOLID 原则。 但这句话实际是什么意思?让我们看看 SOLID 中每个字母在架构里所代表的重要含义,例如: S - 单职责原则 O - 开闭原则 L - Liskov 替换原则 I - 接口分离原则 D - 依赖反转原则这也是依赖注入dependency injection的核心概念。 简单来说,我们需要提供一个类,这个类有它所需要的所有对象,以便实现其功能。 概述 依赖注入听起来像是描述非常复杂的东西的一个术语,但实际上它很简单,看下面这个例子你就明白了: class NoDependencyInjection { private Dependency d; public NoDependencyInjection() { d = new Dependency(); } } class DependencyInjection { private Dependency d; public DependencyInjection(Dependency d) { this.d = d; } } 正如我们所见,第一种情况是我们在构造器里创建了依赖对象,但在第二种情况下,它作为参数被传递给构造器,这就是我们所说的依赖注入dependency injection。这样做是为了让我们所写的类不依靠特定依赖关系的实现,却能直接使用它。 参数传递的目标是构造器,我们就称之为构造器依赖注入;或者是某个方法,就称之为方法依赖注入: class Example { private ConstructorDependency cd; private MethodDependency md; Example(ConstructorDependency cd) { this.cd = cd; //Constructor Dependency Injection } public setMethodDependency(MethodDependency md) { this.md = md; //Method Dependency Injection } } 要是你想总体深入地了解依赖注入,可以看看由Dan Lew发表的精彩的演讲,事实上是这个演讲启迪了这篇概述。 在 Android 平台,当需要框架来处理依赖注入这个特殊的问题时,我们有不同的选择,其中最有名的框架就是Dagger 2。它最开始是由 Square 公司(LCTT 译注:Square 是美国一家移动支付公司)的一些很棒的开发者开发出来的,然后慢慢发展成由 Google 自己开发。首先开发出来的是 Dagger 1,然后 Big G 接手这个项目发布了第二个版本,做了很多改动,比如以注解annotation为基础,在编译的时候完成其任务。 导入框架 安装 Dagger 并不难,但需要导入android-apt插件,通过向项目的根目录下的build.gradle文件中添加它的依赖关系: buildscript{ ... dependencies{ ... classpath ‘com.neenbedankt.gradle.plugins:android-apt:1.8’ } } 然后,我们需要将android-apt插件应用到项目build.gradle文件,放在文件顶部 Android application 那一句的下一行: apply plugin: ‘com.neenbedankt.android-apt’ 这个时候,我们只用添加依赖关系,然后就能使用库及其注解annotation了: dependencies{ ... compile ‘com.google.dagger:dagger:2.6’ apt ‘com.google.dagger:dagger-compiler:2.6’ provided ‘javax.annotation:jsr250-api:1.0’ } 需要加上最后一个依赖关系是因为 @Generated 注解在 Android 里还不可用,但它是原生的 Java 注解。 Dagger 模块 要注入依赖,首先需要告诉框架我们能提供什么(比如说上下文)以及特定的对象应该怎样创建。为了完成注入,我们用@Module注释对一个特殊的类进行了注解(这样 Dagger 就能识别它了),寻找@Provide注解的方法,生成图表,能够返回我们所请求的对象。 看下面的例子,这里我们创建了一个模块,它会返回给我们ConnectivityManager,所以我们要把Context对象传给这个模块的构造器。 @Module public class ApplicationModule { private final Context context; public ApplicationModule(Context context) { this.context = context; } @Provides @Singleton public Context providesContext() { return context; } @Provides @Singleton public ConnectivityManager providesConnectivityManager(Context context) { return (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); } } Dagger 中十分有意思的一点是简单地注解一个方法来提供一个单例(Singleton),就能处理所有从 Java 中继承过来的问题。 组件 当我们有一个模块的时候,我们需要告诉 Dagger 想把依赖注入到哪里:我们在一个组件Component里完成依赖注入,这是一个我们特别创建的特殊注解接口。我们在这个接口里创造不同的方法,而接口的参数是我们想注入依赖关系的类。 下面给出一个例子并告诉 Dagger 我们想要MainActivity类能够接受ConnectivityManager(或者在图表里的其它依赖对象)。我们只要做类似以下的事: @Singleton @Component(modules = {ApplicationModule.class}) public interface ApplicationComponent { void inject(MainActivity activity); } 正如我们所见,@Component 注解有几个参数,一个是所支持的模块的数组,代表它能提供的依赖。这里既可以是Context也可以是ConnectivityManager,因为它们在ApplicationModule类中有声明。 用法 这时,我们要做的是尽快创建组件(比如在应用的onCreate阶段)并返回它,那么类就能用它来注入依赖了: 为了让框架自动生成DaggerApplicationComponent,我们需要构建项目以便 Dagger 能够扫描我们的代码,并生成我们需要的部分。 在MainActivity里,我们要做的两件事是用@Inject注解符对想要注入的属性进行注解,调用我们在ApplicationComponent接口中声明的方法(请注意后面一部分会因我们使用的注入类型的不同而变化,但这里简单起见我们不去管它),然后依赖就被注入了,我们就能自由使用他们: public class MainActivity extends AppCompatActivity { @Inject ConnectivityManager manager; @Override protected void onCreate(Bundle savedInstanceState) { ... ((App) getApplication()).getComponent().inject(this); } } 总结 当然了,我们可以手动注入依赖,管理所有不同的对象,但 Dagger 消除了很多比如模板这样的“噪声”,给我们提供有用的附加品(比如Singleton),而仅用 Java 处理将会很糟糕。 原文发布时间为:2017-12-27 本文来自云栖社区合作伙伴“Linux中国”

优秀的个人博客,低调大师

安卓面试中高级安卓开发工程师总结之——不可忽视的简历加分项

今天我们继续说说简历的问题。如何让自己的简历在众多简历中有更大的优势?如何给自己的简历加分? 说道加分项,这个跟市场有关。现在是2018年夏天。那么近2年来,前端什么比较火呢? 答案很多。但是有几点是可以确定的。指纹识别,人脸识别,语音识别,小视频(你有被快手,抖音中毒吗?),直播(这个最火的应该在2015-2016年),传感器。 这些,请至少尝试几个写一下,哪怕就是一个简单的demo。如果什么都不会,GitHub知道吧,搜索回吧,找几个别人写好的demo,然后下载下来,然后自己再敲一遍,试着改一改。可以吧。如果还不会,找我,你回吧。我的QQ你有吧,我教你行吧。如果你没有我的QQ ,那我给你留好吧578161213.如果还不会,那恭喜你,你已步入高级工程师前列(没有腺),你的高度,不是我等能企及的。 其实这些真的没有什么难的。比如,指纹识别。Google在6.0以后就有指纹识别的API了。FingerPrintManager,具体你可以发挥前端的强项了——面向百度编程。如果你找不到,又想短时间内狂补这个,请点击它:https://blog.csdn.net/csh86277516/article/details/68926931 但是指纹识别有一个很大的坑:因为谷歌在6.0才发布这个API,而在这之前,已经有很多公司自己做了指纹识别的功能,所以,不是所有的设备装的都是谷歌的原生API,所以面临这适配的问题。具体这个坑怎么解决?请点击它:https://www.cnblogs.com/popfisher/p/6063835.html 人脸识别,这个不一定要攻克,因为很多公司都已经攻克了,并且做的非常好。比如非死不可(FaceBook)的DeepFace。阿里也有人脸识别的SDK,我之前用过,不过是收费的,不贵,现在不知道免费了没。谷歌的FaceNet。这个不作为了解项。 语音识别,说到这个很多人又惊呆了。这个算法识别怎么做的,据说很难的。唤醒词都很难。我想说的是,应用在前端,真的不难。集成百度,科大讯飞,或者谷歌的语音识别SDK即可。在这里,博主用实践测试数据告诉大家,谷歌的语音识别最准确,甩科大讯飞,百度语音识别几条街(这么说,好像提高了百度,科大讯飞的档次。)。谷歌语音识别,精准度非常好,错误率非常非常低,并且用方言(部分地区方言,不是所有的)也能识别正确。 传感器。很多开发人员都没有调用过谷歌的传感器的API吧.传感器和canvas结合(别塞尔就好),会达到非常炫酷的效果。谷歌在很早就有了传感器的API,SensorManager就是传感器的一个综合管理类。 Android支持的传感器有如下几种: Sensor.TYPE_ACCELEROMETER加速度传感器 Sensor.TYPE_GYROSCOPE陀螺仪传感器 Sensor.TYPE_LIGHT亮度传感器 Sensor.TYPE_MAGNETIC_FIELD地磁传感器 Sensor.ORIENTATION方向传感器 Sensor.TYPE_PRESSURE压力传感器 Sensor.TYPE_PROXIMITY近程传感器 Sensor.TYPE_TEMPERATURE温度传感器 前提是ADB本身要有这些硬件传感器的支持,否则无效。 相信很多人都有过看直播,或者刷抖音,快手,微博的吧。这几年小视频的火热,一度让腾讯爸爸都做了各种限制(微信朋友圈不让分享这些小视频,同时自己也在做微视,不要脸的腾讯。各种抄袭,盗版,别人盗版还给作者一个活路,他是能把原作者抄死的那种。多少小学生,祖国的未来被他坑害了。)。 所以流媒体的开发应用也能为你的简历加分。这也是博主去面试多家公司得到的消息,要做小视频方向的开发。千真万确。那些公司我就不透露了。所以,赶紧试着写一些视频播放吧。给大家推荐GitHub上十二款最著名的Android播放器开源项目: 详见我的另一篇文章,github上十二款最著名的Android播放器开源项目

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册