Xây dựng Car Location Tracking cho Android với Firebase

Lập Trình
Xây dựng Car Location Tracking cho Android với Firebase

Bài viết được sự cho phép của tác giả Sơn Dương

Trong bài hướng dẫn này, chúng ta sẽ tạo một ứng dụng theo dõi vị trí xe (Car Location Tracking) giống như Grab và Uber. Bài viết này mình sẽ sử dụng Firebase Real-time Database. Tài xế chỉ cần gửi vị trí hiện tại về firebase và khách hàng sẽ cập nhật được vị trí của lái xe trên Google Map.

>>> Xem thêm: Firebase là gì

Mặc dù chúng ta sẽ không phát triển một ứng dụng hoàn thiện như Grab hay Uber. Nhưng mình sẽ hướng dẫn các bạn tự xây dựng một tính năng rất quan trọng đó là cập nhật thời gian thực, hiển thị thông tin tài xế trên ứng dụng khách hàng.

Bài viết sẽ chia làm 2 phần:

  • Phần 1: Xây dựng tính năng gửi location theo thời gian thực cho tài xế (dành cho tài xế).
  • Phần 2: Xây dựng tính năng hiển thị vị trí tài xế theo thời gian thực (dành cho khách hàng).

Kết quả của bài viết này sẽ là ứng dụng Car Location Tracking như bên dưới:

Từng bước xây dựng ứng dụng Car Location Tracking trên Android

#1. Yêu cầu trước khi bắt đầu xây dựng Car Location Tracking

  1. Bạn phải có Google Map API để hiển thị bản đồ. Xem đường link này để lấy API Key
  2. Cần có một Firebase project để sử dụng real-time database. Bạn có thể tạo Firebase project tại đây.

Sau khi bạn đã hoàn thành 2 bước trên thì chuyển tiếp sang bước bên dưới nhé.

#2. Lập trình ứng dụng cho tài xế(Driver App)

Như mình đã nói ở trên, tổng thể ứng dụng Car Location Tracking  sẽ chia làm 2 ứng dụng độc lập. Một ứng dụng dành riêng cho tài xế và một dành riêng cho khách hàng.

Vì vậy, phần 1 của bài viết này, chúng ta sẽ bắt đầu với ứng dụng dành cho tài xế, gọi là Driver App.

Cài đặt thư viện cần thiết

Đầu tiên, hãy thêm thư viện vào build.gradle

implementation 'com.google.android.gms:play-services-location:15.0.1'
implementation 'com.google.android.gms:play-services-maps:15.0.1'
implementation 'com.google.firebase:firebase-database:16.0.1'

Sau đó thêm những permission cần thiết cho ứng dụng. Trong 3 permissions này thì permission về quyền location là bạn cần phải được sự đồng ý của người dùng.

Bạn có thể tham khảo thêm bài viết của mình về cách xin cấp permission trong Android.

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

Ngoài ra, để hiển thị Google Maps trong ứng dụng Car Location Tracking  chúng ta cần thêm các thẻ meta vào trong Manifest file.

<meta-data
    android:name="com.google.android.gms.version"
    android:value="@integer/google_play_services_version" />

<meta-data
    android:name="com.google.android.geo.API_KEY"
    android:value="@string/map_api_key" /> // Change it with your Google Maps API key.

Tất cả những khâu chuẩn bị đã sẵn sàng cho việc hiển thị Google Maps và đọc vị trí của người dùng.

Xây dựng giao diện ứng dụng Car Location Tracking 

Ok, không chần chừ thêm nữa chúng ta hãy cùng bắt tay vào việc lập trình nào. Dưới đây là giao diện người dùng của DriverApp mà chúng ta sẽ tạo.

ung-dung-car-location-tracking-cho-android-1ung-dung-car-location-tracking-cho-android-1

Giao diện rất cơ bản, chúng ta có SwitchCompat dành cho cả tài xế trực tuyến và ngoại tuyến, bên dưới là Google Map.

Để tạo giao diện như trên, các bạn code như bên dưới đây (các bạn code vào file layout là activity_main.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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <FrameLayout
        android:id="@+id/driverStatusLayout"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@color/colorPrimary"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/driverStatusTextView"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_marginStart="15dp"
            android:gravity="center"
            android:text="@string/offline"
            android:textColor="@color/colorIcons"
            android:textSize="22sp" />

       <android.support.v7.widget.SwitchCompat
           android:id="@+id/driverStatusSwitch"
           android:layout_width="wrap_content"
           android:layout_height="match_parent"
           android:layout_gravity="end"
           android:layout_marginEnd="15dp"
           android:checked="false"
           android:theme="@style/SCBSwitch" />

      </FrameLayout>

      <fragment
          android:id="@+id/supportMap"
          android:name="com.google.android.gms.maps.SupportMapFragment"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:layout_below="@+id/driverStatusLayout"
tools:context="spartons.com.frisbeeGo.fragments.MapFragment" />

</RelativeLayout>

MainActivity

Sau khi đã có layout, chúng ta sẽ code để hiển thị map và lấy location. Dưới đây là code cho Activity chính

class MainActivity : AppCompatActivity() {

    companion object {
        private const val MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 2200
    }

    private lateinit var googleMap: GoogleMap
    private lateinit var locationProviderClient: FusedLocationProviderClient
    private lateinit var locationRequest: LocationRequest
    private lateinit var locationCallback: LocationCallback
    private var locationFlag = true
    private var driverOnlineFlag = false
    private var currentPositionMarker: Marker? = null
    private val googleMapHelper = GoogleMapHelper()
    private val firebaseHelper = FirebaseHelper("0000")
    private val markerAnimationHelper = MarkerAnimationHelper()
    private val uiHelper = UiHelper()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val mapFragment: SupportMapFragment = supportFragmentManager.findFragmentById(R.id.supportMap) as SupportMapFragment
        mapFragment.getMapAsync { googleMap = it }
        createLocationCallback()
        locationProviderClient = LocationServices.getFusedLocationProviderClient(this)
        locationRequest = uiHelper.getLocationRequest()
        if (!uiHelper.isPlayServicesAvailable(this)) {
            Toast.makeText(this, "Play Services did not installed!", Toast.LENGTH_SHORT).show()
            finish()
        } else requestLocationUpdate()
        val driverStatusTextView = findViewById<TextView>(R.id.driverStatusTextView)
        findViewById<SwitchCompat>(R.id.driverStatusSwitch).setOnCheckedChangeListener { _, b ->
            driverOnlineFlag = b
            if (driverOnlineFlag) driverStatusTextView.text = resources.getString(R.string.online_driver)
            else {
                driverStatusTextView.text = resources.getString(R.string.offline)
                firebaseHelper.deleteDriver()
            }
        }
    }

    @SuppressLint("MissingPermission")
    private fun requestLocationUpdate() {
        if (!uiHelper.isHaveLocationPermission(this)) {
            ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION)
            return
        }
        if (uiHelper.isLocationProviderEnabled(this))
            uiHelper.showPositiveDialogWithListener(this, resources.getString(R.string.need_location), resources.getString(R.string.location_content), object : IPositiveNegativeListener {
                override fun onPositive() {
startActivity(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS))
                }
            }, "Turn On", false)
locationProviderClient.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper())
    }

    private fun createLocationCallback() {
        locationCallback = object : LocationCallback() {
            override fun onLocationResult(locationResult: LocationResult?) {
               super.onLocationResult(locationResult)
               if (locationResult!!.lastLocation == null) return
               val latLng = LatLng(locationResult.lastLocation.latitude, locationResult.lastLocation.longitude)
               Log.e("Location", latLng.latitude.toString() + " , " + latLng.longitude)
               if (locationFlag) {
                   locationFlag = false
                   animateCamera(latLng)
               }
               if (driverOnlineFlag) firebaseHelper.updateDriver(Driver(lat = latLng.latitude, lng = latLng.longitude))
               showOrAnimateMarker(latLng)
           }
       }
   }

   private fun showOrAnimateMarker(latLng: LatLng) {
       if (currentPositionMarker == null)
           currentPositionMarker = googleMap.addMarker(googleMapHelper.getDriverMarkerOptions(latLng))
       else markerAnimationHelper.animateMarkerToGB(currentPositionMarker!!, latLng, LatLngInterpolator.Spherical())
   }

   private fun animateCamera(latLng: LatLng) {
       val cameraUpdate = googleMapHelper.buildCameraUpdate(latLng)
       googleMap.animateCamera(cameraUpdate, 10, null)
   }

   override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
       super.onRequestPermissionsResult(requestCode, permissions, grantResults)
       if (requestCode == MY_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION) {
           val value = grantResults[0]
           if (value == PERMISSION_DENIED) {
               Toast.makeText(this, "Location Permission denied", Toast.LENGTH_SHORT).show()
               finish()
           } else if (value == PERMISSION_GRANTED) requestLocationUpdate()
       }
    }
}

Mình sẽ giải thích cụ thể tác dụng của các hàm quan trọng:

1.createLocationCallback(): Chúng ta gọi hàm này từ hàm onCreate của MainActivity. Trong hàm LocationCallback, chúng ta sẽ lấy vị trí hiện tại của tài xế,và cập nhật trên Firebase Real-time Database nếu tài xế đang trực tuyến

2.requestLocationUpdates(): Gọi hàm này từ hàm onCreate của MainActivity nếu người dùng đã cài đặt GooglePlayService.

Trong hàm này, chúng ta sẽ cần đoạn mã để yêu cầu người dùng cấp quyền cho Location permission. Sau đó chúng tôi kiểm tra Location provider đã được bật lên hay chưa. Cuối cùng là bắt đầu cập nhật vị trí.

3. showOrAnimateMarker(): chúng ta sẽ kiểm tra xem thử Marker của xe tài xế đã có rồi hay chưa, nếu chưa thì tạo mới  một Marker vào Google Maps. Nếu đã có rồi thì tạo hiệu ứng chuyển động cho Marker đến vị trí mới.

4. animteCamera(): Mục đích chính của hàm này là tạo hiệu ứng và chuyển map về vị trí hiện tại

UiHelper

Class này mình tạo riêng với mục đích sẽ viết những hàm mà mình có thể tái sử dụng nhiều lần. Như tên của class, các hàm liên quan đến UI sẽ được mình để vào đây

class UiHelper {

    fun isPlayServicesAvailable(context: Context): Boolean {
        val googleApiAvailability = GoogleApiAvailability.getInstance()
        val status = googleApiAvailability.isGooglePlayServicesAvailable(context)
        return ConnectionResult.SUCCESS == status
    }

    fun isHaveLocationPermission(context: Context): Boolean {
        return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || ActivityCompat.checkSelfPermission(context, android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(context, android.Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED
    }

    fun isLocationProviderEnabled(context: Context): Boolean {
        val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
        return !locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) && !locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
    }

    fun showPositiveDialogWithListener(callingClassContext: Context, title: String, content: String, positiveNegativeListener: IPositiveNegativeListener, positiveText: String, cancelable: Boolean) {
        buildDialog(callingClassContext, title, content)
                .builder
                .positiveText(positiveText)
                .positiveColor(getColor(R.color.colorPrimary, callingClassContext))
                .onPositive { _, _ -> positiveNegativeListener.onPositive() }
                .cancelable(cancelable)
                .show()
    }

    private fun buildDialog(callingClassContext: Context, title: String, content: String): MaterialDialog {
         return MaterialDialog.Builder(callingClassContext)
                 .title(title)
                 .content(content)
                 .build()
    }


    private fun getColor(color: Int, context: Context): Int {
        return ContextCompat.getColor(context, color)
    }

    fun getLocationRequest() : LocationRequest {
        val locationRequest = LocationRequest.create()
        locationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
        locationRequest.interval = 3000
        return locationRequest
    }
}

Mình sẽ giải thích một số hàm quan trọng:
isPlayServicesAvailable():Hàm này kiểm tra việc người dùng đã cài Google Play Services hay chưa.

isHaveLocationPermission():Kiểm tra xem người dùng có cấp quyền truy cập vị trí (location permission) hay không.

isLocationProviderEnabled(): Kiểm tra xem Location Provider đã được kích hoạt hay chưa. Nếu chưa thì mở Setting và bật Location Provider( Chọn GPS hay Network…)

showPositiveDialogWithListener(): Chức năng tiện ích để hiển thị Dialog khi điện thoại người dùng vì lý do nào đó mà tắt Location Provider

GoogleMapHelper

Class này sẽ gồm những hàm dàng riêng cho map cho Car Location Tracking .

class GoogleMapHelper {

    companion object {
        private const val ZOOM_LEVEL = 18
        private const val TILT_LEVEL = 25
    }

    /**
     * @param latLng in which position to Zoom the camera.
     * @return the [CameraUpdate] with Zoom and Tilt level added with the given position.
     */

    fun buildCameraUpdate(latLng: LatLng): CameraUpdate {
        val cameraPosition = CameraPosition.Builder()
                .target(latLng)
                .tilt(TILT_LEVEL.toFloat())
                .zoom(ZOOM_LEVEL.toFloat())
                .build()
        return CameraUpdateFactory.newCameraPosition(cameraPosition)
    }

    /**
     * @param position where to draw the [com.google.android.gms.maps.model.Marker]
     * @return the [MarkerOptions] with given properties added to it.
     */

    fun getDriverMarkerOptions(position: LatLng): MarkerOptions {
        val options = getMarkerOptions(R.drawable.car_icon, position)
        options.flat(true)
        return options
    }

    private fun getMarkerOptions(resource: Int, position: LatLng): MarkerOptions {
        return MarkerOptions()

 .icon(BitmapDescriptorFactory.fromResource(resource))
                 .position(position)
    }
}

MarkerAnimationHelper

Lớp MarkerAnimationHelper tạo hiệu ứng cho marker khi xe tài xế di chuyển từ vị trí cũ tới vị trí mới.

class MarkerAnimationHelper {

    fun animateMarkerToGB(marker: Marker, finalPosition: LatLng, latLngInterpolator: LatLngInterpolator) {
        val startPosition = marker.position
        val handler = Handler()
        val start = SystemClock.uptimeMillis()
        val interpolator = AccelerateDecelerateInterpolator()
        val durationInMs = 2000f
        handler.post(object : Runnable {
            var elapsed: Long = 0
            var t: Float = 0.toFloat()
            var v: Float = 0.toFloat()
            override fun run() {
                // Calculate progress using interpolator
                elapsed = SystemClock.uptimeMillis() - start
                t = elapsed / durationInMs
                v = interpolator.getInterpolation(t)
                marker.position = latLngInterpolator.interpolate(v, startPosition, finalPosition)
                // Repeat till progress is complete.
                if (t < 1) {
                    // Post again 16ms later.
                    handler.postDelayed(this, 16)
                }
            }
        })
    }
}

FirebaseHelper

Mình sẽ viết những hàm liên quan đến kết nôi Firebase tại class này:

class FirebaseHelper constructor(driverId: String) {

    companion object {
        private const val ONLINE_DRIVERS = "online_drivers"
    }

    private val onlineDriverDatabaseReference: DatabaseReference = FirebaseDatabase
            .getInstance()
            .reference
            .child(ONLINE_DRIVERS)
            .child(driverId)

    init {
        onlineDriverDatabaseReference
                .onDisconnect()
                .removeValue()
    }

    fun updateDriver(driver: Driver) {
        onlineDriverDatabaseReference
                .setValue(driver)
        Log.e("Driver Info", " Updated")
    }

    fun deleteDriver() {
        onlineDriverDatabaseReference
               .removeValue()
    }
}

Trước khi bắt đầu giải thích về class FirebaseHelper, mình muốn cho bạn thấy cấu trúc của Firebase Real-time Database.

ung-dung-car-location-tracking-cho-android-2ung-dung-car-location-tracking-cho-android-2

Mình sẽ giải thích một số hàm quan trọng trong FirebaseHelper.
onlineDriverDatabaseReference(): Khi tạo DatabaseReference, chúng ta cần thêm hai thư mục: một cho các điểm mà các drivers đang online khác, một cho bản thân driver đó.

Chúng ta cần thông báo firebase real-time database để cập nhật thông tin vị trí Driver. Đó chính là lý do tại sao mình lại thiết lập driverId như là top node và là một đối tượng Driver. Lưu ý driverId phải unique

updateDriver(): Cập nhật vị trí mới của Driver firebase real-time database.

deleteDriver(): Loại bỏ driver node khỏi firebase real-time database.

Driver Object

Class này đơn giản là model để mình định nghĩa object driver với các thuộc tính: driverId, lat, lng

data class Driver(val lat: Double, val lng: Double, val driverId: String = "0000")

Bạn có thể thay đổi driverId bằng mã khóa chính của người dùng hoặc bất kỳ thứ gì mà bạn cho là unique

Giải thích thêm: Cách tiếp cận mà mình sử dụng cho 2 mode của tài xế: online và offline tương tự cách mà các ứng dụng Social Media đang làm. Tức là hiển thị trạng thái online cuối cùng, kiểm tra xem người dùng có online hay không, v.v.

Tổng kết

Như vậy là chúng ta đã hoàn thành ứng dụng Car location Tracking phần danh cho tài xế. Toàn bộ source code, các bạn có thể download bên dưới.

Bài viết sau, mình sẽ tiếp tục hướng dẫn các bạn xây dựng phần hiển thị vị trí của tài xế, phần dành cho khách hàng.

Bài viết gốc được đăng tải tại vntalking.com

Xem thêm:

Tìm việc làm IT mọi cấp độ tại Station D

Bài viết liên quan

Thị trường EdTech Vietnam- Nhiều tiềm năng nhưng còn bị bỏ ngỏ tại Việt Nam

Thị trường EdTech Vietnam- Nhiều tiềm năng nhưng còn bị bỏ ngỏ tại Việt Nam

Lĩnh vực EdTech (ứng dụng công nghệ vào các sản phẩm giáo dục) trên toàn cầu hiện nay đã tương đối phong phú với nhiều tên tuổi lớn phân phối đều trên các hạng mục như Broad Online Learning Platforms (nền tảng cung cấp khóa học online đại chúng – tiêu biểu như Coursera, Udemy, KhanAcademy,…) Learning Management Systems (hệ thống quản lý lớp học – tiêu biểu như Schoology, Edmodo, ClassDojo,…) Next-Gen Study Tools (công cụ hỗ trợ học tập – tiểu biểu như Kahoot!, Lumosity, Curriculet,…) Tech Learning (đào tạo công nghệ – tiêu biểu như Udacity, Codecademy, PluralSight,…), Enterprise Learning (đào tạo trong doanh nghiệp – tiêu biểu như Edcast, ExecOnline, Grovo,..),… Hiện nay thị trường EdTech tại Việt Nam đã đón nhận khoảng đầu tư khoảng 55 triệu đô cho lĩnh vực này nhiều đơn vị nước ngoài đang quan tâm mạnh đến thị trường này ngày càng nhiều hơn. Là một trong những xu hướng phát triển tốt, và có doanh nghiệp đã hoạt động khá lâu trong ngành nêu tại infographic như Topica, nhưng EdTech vẫn chỉ đang trong giai đoạn sơ khai tại Việt Nam. Tại Việt Nam, hệ sinh thái EdTech trong nước vẫn còn rất non trẻ và thiếu vắng nhiều tên tuổi trong các hạng mục như Enterprise Learning (mới chỉ có MANA), School Administration (hệ thống quản lý trường học) hay Search (tìm kiếm, so sánh trường và khóa học),… Với chỉ dưới 5% số dân công sở có sử dụng một trong các dịch vụ giáo dục online, EdTech cho thấy vẫn còn một thị trường rộng lớn đang chờ được khai phá. *** Vừa qua Station D đã công bố Báo cáo Vietnam IT Landscape 2019 đem đến cái nhìn toàn cảnh về các ứng dụng công...

By stationd
Bộ cài đặt Laravel Installer đã hỗ trợ tích hợp Jetstream

Bộ cài đặt Laravel Installer đã hỗ trợ tích hợp Jetstream

Bài viết được sự cho phép của tác giả Chung Nguyễn Hôm nay, nhóm Laravel đã phát hành một phiên bản chính mới của “ laravel/installer ” bao gồm hỗ trợ khởi động nhanh các dự án Jetstream. Với phiên bản mới này khi bạn chạy laravel new project-name , bạn sẽ nhận được các tùy chọn Jetstream. Ví dụ: API Authentication trong Laravel-Vue SPA sử dụng Jwt-auth Cách sử dụng Laravel với Socket.IO laravel new foo --jet --dev Sau đó, nó sẽ hỏi bạn thích stack Jetstream nào hơn: Which Jetstream stack do you prefer? [0] Livewire [1] inertia > livewire Will your application use teams? (yes/no) [no]: ... Nếu bạn đã cài bộ Laravel Installer, để nâng cấp lên phiên bản mới bạn chạy lệnh: composer global update Một số trường hợp cập nhật bị thất bại, bạn hãy thử, gỡ đi và cài đặt lại nha composer global remove laravel/installer composer global require laravel/installer Bài viết gốc được đăng tải tại chungnguyen.xyz Có thể bạn quan tâm: Cài đặt Laravel Làm thế nào để chạy Sql Server Installation Center sau khi đã cài đặt xong Sql Server? Quản lý các Laravel route gọn hơn và dễ dàng hơn Xem thêm Tuyển dụng lập trình Laravel hấp dẫn trên Station D

By stationd
Principle thiết kế của các sản phẩm nổi tiếng

Principle thiết kế của các sản phẩm nổi tiếng

Tác giả: Lưu Bình An Phù hợp cho các bạn thiết kế nào ko muốn làm code dạo, design dạo nữa, bạn muốn cái gì đó cao hơn ở tầng khái niệm Nếu lập trình chúng ta có các nguyên tắc chung khi viết code như KISS , DRY , thì trong thiết kế cũng có những nguyên tắc chính khi làm việc. Những nguyên tắc này sẽ là kim chỉ nam, nếu có tranh cãi giữa các member trong team, thì cứ đè nguyên tắc này ra mà giải quyết (nghe hơi có mùi cứng nhắc, mình thì thích tùy cơ ứng biến hơn) Tìm các vị trí tuyển dụng designer lương cao cho bạn Nguyên tắc thiết kế của GOV.UK Đây là danh sách của trang GOV.UK Bắt đầu với thứ user cần Làm ít hơn Thiết kế với dữ liệu Làm mọi thứ thật dễ dàng Lặp. Rồi lặp lại lần nữa Dành cho tất cả mọi người Hiểu ngữ cảnh hiện tại Làm dịch vụ digital, không phải làm website Nhất quán, nhưng không hòa tan (phải có chất riêng với thằng khác) Cởi mở, mọi thứ tốt hơn Bao trừu tượng luôn các bạn, trang Gov.uk này cũng có câu tổng quát rất hay Thiết kế tốt là thiết kế có thể sử dụng. Phục vụ cho nhiều đối tượng sử dụng, dễ đọc nhất nhất có thể. Nếu phải từ bỏ đẹp tinh tế – thì cứ bỏ luôn . Chúng ta tạo sản phẩm cho nhu cầu sử dụng, không phải cho người hâm mộ . Chúng ta thiết kế để cả nước sử dụng, không phải những người đã từng sử dụng web. Những người cần dịch vụ của chúng ta nhất là những người đang cảm thấy khó sử dụng dịch...

By stationd
Applicant Tracking System là gì? ATS hoạt động ra sao

Applicant Tracking System là gì? ATS hoạt động ra sao

Công nghệ phát triển hướng đến giải quyết và cải tiến cho mỗi quy trình, hoạt động của doanh nghiệp cũng như đời sống. Đối với lĩnh vực tuyển dụng, sự xuất hiện của phần mềm ATS (Applicant Tracking System) mang đến nhiều thay đổi đáng kể, cả đối với nhà tuyển dụng và ứng viên. Vậy phần mềm ATS là gì? Chúng được sử dụng ra sao? Những thắc mắc về phần mềm ATS trong tuyển dụng sẽ được Station D giải đáp tại bài viết dưới đây. Hệ thống sàng lọc ứng viên ATS (Applicant Tracking System) Applicant Tracking System là gì? Applicant Tracking System (ATS) hay còn gọi là Hệ thống quản lý hồ sơ ứng viên là phần mềm quản lý quy trình tuyển dụng từ đầu đến cuối một cách tự động hóa. ATS được thiết kế để giúp nhà tuyển dụng tiết kiệm thời gian và chi phí trong việc thu thập, sắp xếp và sàng lọc hồ sơ các ứng viên. Các tính năng nổi bật của Applicant Tracking System Các tính năng nổi bật của Applicant Tracking System Applicant Tracking System (ATS) là một công cụ quan trọng giúp các công ty quản lý quy trình tuyển dụng hiệu quả hơn. Cùng chúng tôi điểm qua các tính năng nổi bật của ATS: Sàng lọc và quản lý hồ sơ ứng viên Khả năng tìm kiếm và sàng lọc ứng viên là một trong các tính năng nổi bật nhất của ATS. Với việc sử dụng từ khóa và tiêu chí cụ thể, hệ thống có thể nhanh chóng tìm kiếm và sàng lọc các hồ sơ phù hợp, tự động loại bỏ những ứng viên không đạt yêu cầu. Điều này giúp nhà tuyển dụng tập trung vào những ứng viên...

By stationd
Hiểu về trình duyệt – How browsers work

Hiểu về trình duyệt – How browsers work

Bài viết được sự cho phép của vntesters.com Khi nhìn từ bên ngoài, trình duyệt web giống như một ứng dụng hiển thị những thông tin và tài nguyên từ server lên màn hình người sử dụng, nhưng để làm được công việc hiển thị đó đòi hỏi trình duyệt phải xử lý rất nhiều thông tin và nhiều tầng phía bên dưới. Việc chúng ta (Developers, Testers) tìm hiểu càng sâu tầng bên dưới để nắm được nguyên tắc hoạt động và xử lý của trình duyệt sẽ rất hữu ích trong công việc viết code, sử dụng các tài nguyên cũng như kiểm thử ứng dụng của mình. Cách để npm packages chạy trong browser Câu hỏi phỏng vấn mẹo về React: Component hay element được render trong browser? Khi hiểu được cách thức hoạt động của trình duyệt chúng ta có thể trả lời được rất nhiều câu hỏi như: Tại sao cùng một trang web lại hiển thị khác nhau trên hai trình duyệt? Tại sao chức năng này đang chạy tốt trên trình duyệt Firefox nhưng qua trình duyệt khác lại bị lỗi? Làm sao để trang web hiển thị nội dung nhanh và tối ưu hơn một chút?… Hy vọng sau bài này sẽ giúp các bạn có một cái nhìn rõ hơn cũng như giúp ích được trong công việc hiện tại. 1. Cấu trúc của một trình duyệt Trước tiên chúng ta đi qua cấu trúc, thành phần chung và cơ bản nhất của một trình duyệt web hiện đại, nó sẽ gồm các thành phần (tầng) như sau: Thành phần nằm phía trên là những thành phần gần với tương tác của người dùng, càng phía dưới thì càng sâu và nặng về xử lý dữ liệu hơn tương tác. Nhiệm...

By stationd
Tối ưu tỉ lệ chuyển đổi với Google Optimize và Google Analytics

Tối ưu tỉ lệ chuyển đổi với Google Optimize và Google Analytics

Tối ưu tỷ lệ chuyển đổi là một trong những yếu tố quan trọng mà một Growth Marketer không thể bỏ qua. Google Analytics là công cụ hữu hiệu và phổ biến nhất giúp chúng ta theo dõi, đo lường và tối ưu tỷ lệ chuyển đổi. Số liệu từ Google Analytics giúp chúng ta nhìn ra điểm cần thay đổi để tăng tỷ lệ chuyển đổi. Theo dõi chuyển đổi là quá trình thiết yếu nhưng cũng rất “khó nhằn”. Ngày nay, mọi người đang sử dụng điện thoại di động nhiều hơn và thường xuyên hơn khi mua hàng trực tuyến. Việc tối ưu hóa website phiên bản di động rất quan trọng. Traffic đến từ mobile khá lớn và làm ảnh hưởng nhiều tới tỷ lệ chuyển đổi. Để xem website của bạn có hoạt động tốt trên mobile hay không như thế nào? Làm gì khi tỷ lệ chuyển đổi giảm? Website phiên bản di động của bạn có nội dung hấp dẫn không? Không có những nút điều hướng hành động? Trang bị lỗi …, những điều này có thể ảnh hưởng tới tỉ lệ chuyển đổi Google Analytics, hiểu được điều này, bạn sẽ có cơ sở để khắc phục và hoàn thiện website của mình. Hãy cùng đến với chủ đề “DÙNG GOOGLE ANALYTICS TỐI ƯU TỈ LỆ CHUYỂN ĐỔI TRÊN MOBILE” với sự chia sẻ của diễn giả Nguyễn Minh Đức, CEO IM GROUP tại Vietnam Mobile Day 2018 nhé

By stationd